Single sign-on (SSO / OIDC)
OpenID Connect (OIDC) lets PhotoStructure ask an identity provider to confirm who signed in. It is a good fit if you already run Keycloak, Authentik, or another provider for your household.
OIDC is one complete PhotoStructure mode, not an extra button next to email codes. The configured modes are mutually exclusive:
none | email | oidc
In oidc, email-code login is disabled. A provider outage does not silently
switch the library to email or none.
Identity and access are separate
An OIDC provider answers one question: “Which external identity signed in?” PhotoStructure answers the rest:
- Is this identity enrolled in this library?
- Is the user enabled?
- Is the user a visitor, contributor, or owner?
- Must old PhotoStructure sessions end after a security change?
Keycloak can manage application roles and groups, but PhotoStructure will not consume them. Google and Microsoft Entra do not need a PhotoStructure role claim either.
This keeps one role authority inside each PhotoStructure library. Owners manage roles in PhotoStructure for both email and OIDC identities.
How PhotoStructure identifies an OIDC user
PhotoStructure keys an OIDC identity by two signed values:
- the provider’s exact issuer identifier; and
- the provider’s stable subject identifier for that person.
Together, they form the OIDC (issuer, subject) identity.
Email and display name can help the interface explain who signed in. They are not normal identity-linking keys. PhotoStructure never joins an OIDC identity to an email-code identity merely because their email addresses match.
First-owner setup
OIDC login alone cannot claim a new or upgraded library.
For Docker, the local operator configures:
PS_AUTH_MODE=oidc;- one exact
PS_BOOTSTRAP_OWNER_EMAIL; - one complete OIDC provider and client configuration; and
- an exact
PS_EXTERNAL_ORIGINfor the browser-facing deployment.
Desktop and Node collect the logical equivalents through their local control
planes. Missing or invalid configuration derives authSetupRequired. A
browser request, source IP, loopback connection, startup timer, provider role,
or first successful callback cannot choose the provider or owner.
The first owner completes Authorization Code login through the configured
provider. PhotoStructure validates the whole flow and requires an exact match
for the locally configured bootstrap email with the JSON Boolean claim
email_verified: true. One transaction creates the owner and binds (issuer, subject).
The verified email provides first-owner eligibility only. Later login finds
the owner solely by (issuer, subject). A later email match cannot relink,
merge, replace, or create an identity.
There is no setup claim code or browser first-writer flow.
Signing in and enrolling more people
After bootstrap:
- a known, locally enabled identity signs in with its current PhotoStructure role;
- a locally disabled identity is denied; and
- an unknown identity receives no library role or authorized session.
To enroll someone, an owner creates one bounded pending enrollment with an
expected verified email and a PhotoStructure role. The first fully validated
login with that exact verified email consumes the enrollment and binds its
(issuer, subject) identity.
Without a matching pending enrollment, a valid provider login grants nothing. PhotoStructure does not support open registration, a default visitor role, provider role mapping, a bearer invitation link, or general email auto-linking.
Local role or security changes advance the user’s sessionVersion, a counter
stored with each authenticated session. A stale session or open event stream
stops working when its version no longer matches.
Provider requirements
The finished PhotoStructure interface will display the exact callback URL and configuration fields. Do not guess them from this preview.
The approved OIDC profile requires:
- Authorization Code flow with query response mode;
- PKCE
S256, state, and nonce; - exact issuer, audience, signature, and timestamp validation;
- an exact callback URL without wildcards;
- one locally configured provider; and
- a validated external HTTPS PhotoStructure origin.
Desktop and server deployments may need different public or confidential client profiles. A secret embedded in a Desktop app is not secret. We will name exact provider switches only after testing every packaged edition.
PhotoStructure will limit discovery and signing-key fetches by time, size, redirects, content type, fanout, and cache policy. A locally configured private Keycloak server is a supported design target, but browser input can never choose a server-side fetch destination.
OIDC state and the short-lived authorization code appear only in the redirects required by the protocol. PhotoStructure redacts both before application logging, consumes them once, and redirects to a clean URL. Tokens, client secrets, and identity claims never enter URLs, logs, diagnostics, or analytics.
Keycloak
Keycloak is the main self-hosted provider we expect people to use. The future
guide will cover a deployment-specific OpenID Connect client, Standard Flow,
PKCE S256, the canonical realm issuer, and the exact callback URL supplied by
PhotoStructure.
Do not create PhotoStructure client roles or a role protocol mapper. Keycloak authenticates the identity; PhotoStructure owners enroll it and assign its local role.
If Keycloak brokers Google, Microsoft, LDAP, or another directory, Keycloak is PhotoStructure’s issuer. Upstream email, groups, and roles still do not grant library access.
Google and Microsoft Entra
Direct Google or Microsoft Entra OIDC can use the same identity-only contract. Neither provider needs to emit a PhotoStructure role.
Each provider still needs a tested guide for its issuer, audience, client
profile, callback, and logout behavior before PhotoStructure can claim support.
Bootstrap and enrollment require a truthful JSON Boolean email_verified
claim. Hosted-domain, group, and application-role claims do not grant a local
PhotoStructure role.
Provider outages, mode changes, and recovery
Disabling someone at the provider prevents future provider login. It does not
rewrite the person’s PhotoStructure role or prove that every existing session
has ended. PhotoStructure separately enforces its local enabled state, role,
session expiry, logout, logout-all, and sessionVersion.
During a provider outage, new login fails closed. Existing PhotoStructure sessions continue until their normal expiry or local invalidation. Email does not become a fallback login method.
Changing from OIDC to email is a local deployment operation followed by a restart. If the provider is permanently unavailable, the host operator invokes the local lost-owner recovery operation and selects one exact recovery email. That inbox must complete code proof. There is no setup claim, standing recovery token, remote mode switch, or supported database-edit procedure.
Security checklist for the finished guide
Before we replace this preview with setup steps, the released implementation must prove all of the following:
- PhotoStructure supplies the callback URL;
- browser-facing PhotoStructure and provider origins use valid HTTPS;
- token issuer and audience match the configured provider and client exactly;
- callback state belongs to the same short-lived browser flow and cannot be replayed in another browser;
- provider roles, groups, and ordinary email matches cannot grant a local role;
- unknown identities receive no library access; and
- client secrets, tokens, codes, and session cookies stay out of URLs, logs, diagnostics, and library archives.
Exact loopback Desktop and loopback-only Node are the only planned plain-HTTP sign-in profiles. A reverse proxy forwarding to a loopback backend is an external deployment and does not qualify.
See also
- Library users, sign-in, and permissions: modes, roles, bootstrap, and email-code sign-in
- Remote access: HTTPS, VPN, and reverse-proxy guidance
- Environment variables: current deployment configuration
