Commit graph

13330 commits

Author SHA1 Message Date
Michael Mayer
6cb38abb43 Settings: Add UI OpenOnHover setting for hover menus #5650 2026-06-10 02:48:10 +00:00
Michael Mayer
14edf40abd OIDC: Drop residual cluster-OIDC gate on provisioned account email 2026-06-10 02:13:27 +00:00
Michael Mayer
a8849d111d Frontend: Scope instance role-select menu font-size to its overlay 2026-06-10 01:18:22 +00:00
Michael Mayer
71aeb26137 OIDC: Store email only when the token marks it verified 2026-06-10 00:04:26 +00:00
Michael Mayer
1bbb915167 Rules: Note untranslated technical labels and shared option labels 2026-06-09 22:47:57 +00:00
Michael Mayer
c61cc18e67 Frontend: Share role/provider option labels, untranslate identifiers 2026-06-09 22:47:57 +00:00
Michael Mayer
10dc504427 Frontend: Regenerate translation catalogs 2026-06-09 22:47:44 +00:00
Michael Mayer
03a7c7c37b Rules: Note FeatureSettings flag cascade and app-password identification 2026-06-09 17:36:30 +00:00
Michael Mayer
cb48c209b6 Auth: Gate app passwords behind a settings feature flag #5647
Adds an AppPasswords feature flag (default on) so app passwords can be disabled, closing the bypass where an app password minted by an OIDC-provisioned user keeps working after the account expires. When off, minting (oauth/token and auth add) is rejected and existing app passwords are no longer accepted on the REST API (403) or WebDAV (401). App passwords are identified by their session auth provider (IsApplication), covering every grant type. The Apps and Devices affordance is hidden for sessions that may not change a password; operators can force it off via PHOTOPRISM_DISABLE_FEATURES.
2026-06-09 17:36:24 +00:00
Michael Mayer
d0d956410c OIDC: Scope the RP state cookie to the instance OIDC base path
The relying-party state and PKCE cookies were left at the zitadel default Path=/, so on a shared-domain proxy (instances served under /i/<tenant>) they reached the callback only if the proxy rewrote the Set-Cookie path. They are now scoped to <base>/api/v1/oidc, covering both the login leg and the redirect callback, so they survive to the callback regardless of any proxy rewrite.
2026-06-09 15:36:35 +00:00
Michael Mayer
7881849b63 Cluster: Redirect cluster-OIDC sign-out to the Portal login
A user who signed in via cluster OIDC now returns to the Portal login (re-auth via OIDC) on sign-out instead of the instance's local form. The /logout route awaits the cluster-wide sign-out so the Portal OP cookie is cleared before the redirect, avoiding a silent re-SSO.
2026-06-09 15:36:24 +00:00
Michael Mayer
7232cc23a4 OIDC: Render a branded page when the RP code exchange fails
The OIDC RP callback handler returned without writing a response when
CodeExchangeUserInfo failed (e.g. a missing/expired state cookie), leaving
the raw zitadel error ("failed to get state: http: named cookie not present")
on screen with no way forward. CodeExchangeUserInfo now runs the exchange
against a recorder so the handler's raw error never reaches the browser, and
the callback renders the branded auth.gohtml page (which returns the user to
login) instead of a dead-end. Set-Cookie headers from a successful exchange
are still propagated.
2026-06-09 13:11:02 +00:00
Michael Mayer
6d0add2e69 Cluster: Address review findings on sign-out, OIDC bootstrap, errors
- /logout route guard now signs out cluster-wide: session.signOut shares
  revokePeerSessions with logoutEverywhere, so a direct /logout revokes peer
  sessions and clears shared storage too, not just the current session.
- Decouple PHOTOPRISM_CLUSTER_OIDC from the auto-join/theme toggles: InitConfig
  now always runs resolveNodeOIDCClient for an instance (join/theme moved into
  bootstrapClusterNode), so a registered node re-wires its OIDC RP on restart
  even with both bootstrap toggles disabled.
- Map redirected OAuth server_error / temporarily_unavailable to ErrUnexpected
  instead of "Invalid credentials", so a Portal-side failure isn't shown as a
  credential problem.
2026-06-09 10:38:22 +00:00
Michael Mayer
b88429f557 Cluster: Compact code comments in the OIDC follow-up changes 2026-06-09 10:20:17 +00:00
Michael Mayer
2d2dc47968 Frontend: Fix cluster sign-out to clear peer tokens from raw storage
logoutEverywhere read and cleared peer sessions through this.localStorage /
this.sessionStorage, which are NamespacedStorage wrappers (getAppStorage).
Enumerating or clearing a cross-namespace key through a wrapper double-prefixed
it (pp🅰️pp🅱️...), so peer session.token keys were left behind after Sign Out
even though the server sessions were revoked. Unwrap to the raw underlying store
first. The vitest case now constructs the session with a NamespacedStorage, as
the app does, so it would catch the regression.
2026-06-09 10:09:05 +00:00
Michael Mayer
872af5d8fd Cluster: Add cluster OIDC, branded authorize errors, and sign-out
Implements three OIDC follow-ups from specs/portal/cluster-oidc.md:

- Zero-touch instance OIDC RP config: PHOTOPRISM_CLUSTER_OIDC derives the
  OIDC RP client_id/secret from the node client credentials and defaults
  the issuer to the instance's own origin (scheme.OriginURL), so a single
  boot reaches a working Portal login. Explicit oidc-client/secret/uri win.
- Human-friendly authorize errors: redirectable errors 302 back to the
  validated redirect_uri; non-redirectable ones content-negotiate a
  branded HTML page (oauth-error.gohtml) vs JSON via shared helpers in
  internal/api/oauth_error.go. The RP callback surfaces inbound ?error=.
- Cluster-wide Sign-Out (Tier 2): session.logoutEverywhere fans out a
  best-effort DELETE per peer session, clears their storage, then signs
  out locally; session-delete clears the OP cookie on any shared-domain
  OIDC-against-Portal node (OIDCSessionCookieClearPath).
2026-06-09 09:39:01 +00:00
Michael Mayer
0a9e40d659 Frontend: Open the instance frontend route in the nav switcher 2026-06-09 08:16:14 +00:00
Michael Mayer
aae8137168 Cluster: Drop SiteCaption from the node DisplayName fallback 2026-06-09 07:56:45 +00:00
Michael Mayer
02819e6688 Frontend: Hide the Portal root path in the instance switcher 2026-06-09 07:23:09 +00:00
Michael Mayer
680469e781 Cluster: Add a per-instance node DisplayName field 2026-06-09 07:23:05 +00:00
Michael Mayer
680f1fef51 Auth: Drop the hard seed-admin lock in User.SaveForm 2026-06-09 04:18:36 +00:00
Michael Mayer
8f7866617a Docs: Document the API JSON field-casing convention
Add the TitleCase (entity-backed) vs camelCase (generated/artificial) rule to the
API package guide and the api-and-config rule's API Shape Checklist.
2026-06-09 03:18:15 +00:00
Michael Mayer
96f84c37e2 Frontend: Point the portal vitest alias at the cluster-instance model 2026-06-09 03:18:15 +00:00
Michael Mayer
427313c6c7 Frontend: Refine the navigation auth menu and instance switcher
Show the instance app icon and base path in the switcher, persisting the icon
alongside the SiteUrl and title per namespace (default to the logo when none is
set). Add a Manage Account item gated on the account feature, and open the menu
on hover like the other action menus.
2026-06-09 00:41:43 +00:00
Michael Mayer
c3c75dad18 Frontend: Rename PUserMenu to PAuthMenu and add a nav menu affordance
Move component/navigation/user-menu.vue to component/auth/menu.vue (and the
matching Vitest), and add an mdi-dots-horizontal affordance icon to the user
info row so the avatar overlay menu is discoverable.
2026-06-08 23:38:22 +00:00
Michael Mayer
c13c26002e Frontend: Switch menu/action icons from vertical to horizontal dots
Replaces mdi-dots-vertical with mdi-dots-horizontal for the navigation
mobile menu trigger and the p-action-menu default icon, and rotates the
lightbox PhotoSwipe menu-button SVG to match (kept centered in its 16x16
viewBox).
2026-06-08 23:08:36 +00:00
Michael Mayer
f3f358b766 Tests: Remove obsolete navigation showAccountSettings cases
showAccountSettings moved out of navigation.vue when the instance switcher
became the shared user menu; account settings is now a declarative sidebar
link. The avatar menu stays covered by user-menu.test.js.
2026-06-08 22:59:17 +00:00
Michael Mayer
927caa362b Frontend: Redirect CE admin/cluster/instances stubs to default route
These pages only exist as Plus/Pro/Portal overlays; in editions without
the overlay they now redirect to the user's default route via
$session.getDefaultRoute() instead of rendering a placeholder.
2026-06-08 22:59:13 +00:00
Ömer Duran
f07da222cf
Cluster: Polish the Portal admin accounts and instances UI 2026-06-08 15:47:44 +02:00
Ömer Duran
36c1bfcfb4 Tests: Resolve portal common/user-format overlay in vitest config
Add the per-file alias so portal vitest resolves the shared common/user-format formatters to the portal overlay, matching the existing common/instance-grants entry.
2026-06-08 11:14:38 +00:00
Ömer Duran
166b9513f1
Frontend: Hide the Places settings card on the Portal 2026-06-05 21:03:40 +02:00
Ömer Duran
1b5b54aaad
Frontend: Style the Portal Cluster Access role selector 2026-06-05 21:03:39 +02:00
Ömer Duran
75d4419b9b
Frontend: Relabel the user menu logout action to "Sign Out" 2026-06-05 21:03:39 +02:00
Ömer Duran
eefc11a31b
Frontend: Reject non-http(s) URLs in the instance switcher 2026-06-05 21:03:39 +02:00
Ömer Duran
3652ae7b76
Frontend: Add instance selector route with a CE stub page 2026-06-05 21:03:39 +02:00
Ömer Duran
8e73783ddd
Tests: Drop blank lines between Portal OpenID config subtests 2026-06-05 21:03:38 +02:00
Ömer Duran
ce5e10fef3
API: Set Cache-Control no-store on the session response 2026-06-05 21:03:38 +02:00
Ömer Duran
3e5522aa80
Auth: Set the OIDC OP session cookie for user sessions only
Skip user-less sessions whose signal NoUser() rejects, warn on the ephemeral key fallback, and keep a manager's delete-by-ref-id from wiping its own OP cookie.
2026-06-05 21:03:38 +02:00
Ömer Duran
cdc671677d
Auth: Let the cluster JWT sync privilege-level user fields
Thread admin authorization into SaveForm via byAdmin so the user-less cluster service principal can persist login/role/WebDAV instead of silently dropping them.
2026-06-05 21:03:38 +02:00
Ömer Duran
fddab77506
Auth: Apply Portal-granted role on OIDC instance login
Honor the pp_role claim (gated by pp_issuer_kind) over the group mapping so an instance login reflects the role the Portal granted, restricted to federatable roles.
2026-06-05 21:03:36 +02:00
Michael Mayer
366e576087 Frontend: Open the navigation user menu above the avatar
The explicit overlay z-index already keeps the teleported menu above the sidebar,
so anchor it back to location=top (above the avatar) instead of opening it to the
right of the navigation.
2026-06-03 21:01:49 +00:00
Michael Mayer
454998711e Frontend: Keep the navigation user menu above the sidebar
The menu content teleports to body, so a nav-scoped z-index does not apply. Set
an explicit overlay z-index and open it beside the drawer (location=end) so it is
no longer partially covered by the navigation.
2026-06-03 19:52:00 +00:00
Michael Mayer
dffb81ab31 Frontend: Move the instance switcher into the shared navigation user menu
Add a shared component/navigation/user-menu.vue (Settings / Switch Instance /
Log Out) wired into the navigation avatar, so every edition gets the switcher.
Instances record their identity under their storage namespace at session auth and
clear it on logout (common/instances + session.js), and the menu lists same-origin
peers that hold a live session. Keeps the distinctive base-path instanceLabel and
the post-login redirect loop guard. Replaces the bootstrap-time directory recording.
2026-06-03 19:42:56 +00:00
Michael Mayer
368ef7912b Auth: Store a signed session reference in the OP cookie, not the token
The OP session cookie stored the raw bearer token with a full-session lifetime,
so a leak of the cookie value was a usable credential against the whole API. It
now carries an HMAC-signed reference to the session id (a hash of the token,
useless as a bearer) with a 10-minute TTL refreshed on each session GET. The key
is persisted under the Portal config keys directory. The authorize handler
resolves the session via OIDCSessionCookieSession. Needs a coordinated SSO e2e
round in the Portal test env before merge.
2026-06-03 19:15:45 +00:00
Michael Mayer
be82697be2 Frontend: Alias portal common/instance-grants in the portal vitest config
Lets the Portal admin/users.vue page (which keeps the common/instance-grants
overlay) be imported under the portal vitest config so its methods can be
unit-tested.
2026-06-03 19:05:11 +00:00
Michael Mayer
d48ef12c99 Auth: Keep OIDC RP state and PKCE cookies Secure on HTTPS
Gate the OIDC relying-party cookie handler's WithUnsecure() on the existing
insecure flag instead of applying it unconditionally, so the round-trip state
(CSRF defense) and PKCE code_verifier cookies retain the Secure attribute on
HTTPS deployments. Addresses audit finding OJW-260603-02 (GO-HTTP-005).
2026-06-03 16:10:39 +00:00
Michael Mayer
51c4eed71e Auth: Serve Portal OIDC OP endpoints under /api/v1/oauth/* #4368 #4369
Move the Portal OIDC OP authorize/token/userinfo onto the shared CE
/api/v1/oauth/* routes (the reserved #4368/#4369 stubs) and drop the parallel
root /oauth/* set.

- Add overridable handler hooks (oauth_handlers.go) mirroring
  server.WebDAVHandler; OAuthToken delegates only the authorization_code grant
  (gated on a form content type) and keeps client_credentials/password/session
  unchanged; userinfo now serves GET and POST.
- Scope the OP session cookie to /api/v1/oauth (base-path aware) so the browser
  still sends it to the moved authorize endpoint instead of looping.
- Stop the proxy re-scoping instance->Portal authorize redirects
  (portalRootPathPrefixes -> /api/v1/oauth/).
- Point discovery at issuer + /api/v1/oauth/*; fix the JWKS URL base-path
  doubling for sub-path deployments (also in CE openid.go).
2026-06-03 15:28:05 +00:00
Michael Mayer
6b8ae165fb Auth: Accept EdDSA ID tokens in the OIDC relying party 2026-06-03 17:12:59 +02:00
Michael Mayer
c908ec5563 Auth: Advertise use and alg in the Ed25519 JWKS 2026-06-03 17:12:59 +02:00
Michael Mayer
b213d1efdd Frontend: Break post-login redirect loops on the login route 2026-06-03 17:12:59 +02:00