Skip to main content

๐Ÿ”‘ Single sign-on (OIDC)

LibrePhotos can hand login over to an external identity provider that speaks OpenID Connect โ€” Keycloak, Authentik, Authelia, Zitadel, Okta, Google, Entra ID and so on. After the provider confirms who someone is, LibrePhotos issues its own normal session, so everything downstream behaves exactly as it does for a password login.

Username-and-password login keeps working alongside it. Turning SSO on does not take anyone's existing way of logging in away.

What you need before startingโ€‹

  • Your server's public URL, set as the FRONTEND_BASE_URL environment variable (for example https://photos.example.com). This one is not optional. The reverse proxy in front of the backend rewrites the Host header, so LibrePhotos genuinely cannot work out its own public address from an incoming request โ€” it has to be told. Without it the login button reports that SSO is not fully configured.
  • An email provider configured in Site Settings, if you want LibrePhotos to create accounts on first login. See the email settings page. Without email configured, LibrePhotos will not create accounts over SSO, because it has no way to act on the provider's claim that an address is verified.

Registering LibrePhotos with your identity providerโ€‹

Create a confidential client (one with a client secret) at your provider and give it this redirect URI:

https://your-librephotos-url/api/accounts/oidc/<provider_id>/login/callback/

<provider_id> is a short name you choose in the next step โ€” keycloak, authentik, google, whatever you like. It must match exactly.

Note down the client ID, the client secret, and the provider's issuer URL (the one whose /.well-known/openid-configuration document describes it).

Telling LibrePhotos about the providerโ€‹

In the Django admin (/api/django-admin/), under Social applications, add one:

FieldValue
ProviderOpenID Connect
Provider IDthe <provider_id> you used in the redirect URI
Namewhat the login button should say, if you add more than one provider
Client idfrom your provider
Secret keyfrom your provider
Settings{"server_url": "https://your-idp/realms/example"} โ€” the issuer URL
Sitespick your site

Then, in the app, open Admin Area > Site Settings and find the Single sign-on group. Each change saves at once. New in 1.5.0 Up to 1.4.0 these switches were only on the Constance page of the Django admin, under the names in brackets.

  • Show the single sign-on button (OIDC_ENABLED): the on/off switch. While it is off, the login button is hidden and the SSO endpoints are inactive, so switching it off is a real way to stop SSO logins.
  • Button label (OIDC_BUTTON_LABEL): the text on the login button, e.g. "Sign in with Keycloak", at most 64 characters. With more than one provider each button shows its provider's name instead.
  • Allow sign-ups over single sign-on (OIDC_ALLOW_SIGNUP): whether a first-time SSO login may create a new LibrePhotos account. Off by default; also requires email to be configured.

The provider itself is only added and changed in the Django admin, as above. The group lists the providers it finds, and warns under the switches when SSO cannot work yet: no provider attached to this site, no public URL (FRONTEND_BASE_URL), or, for sign-ups, no email set up.

How accounts are matched upโ€‹

On a first SSO login, LibrePhotos looks for an existing account whose email matches the one the provider reports:

  • A single matching account, and the provider says the address is verified โ†’ the two are linked, and from then on the provider's stable user id identifies that person.
  • The provider has not verified the address โ†’ refused. Otherwise anyone able to register your email address at the provider could claim your account.
  • Two or more accounts share the address โ†’ refused, whether or not sign-ups over SSO are on, because there is no single right answer. LibrePhotos does not create yet another account with the same address either.
  • The provider sends no address โ†’ refused, since there is nothing to match on and no verified address to create an account with.
  • No matching account โ†’ a new one is created only if OIDC_ALLOW_SIGNUP is on and email is configured. Otherwise create the account first (or set its email to match) and let the user sign in again.

Accounts created through SSO are never administrators. Grant that in the admin afterwards if you want it.

Using LDAP accountsโ€‹

LibrePhotos does not connect to an LDAP directory itself. Instead, point your identity provider at the directory and LibrePhotos at the provider: people then log in with their directory username and password at the provider, and LibrePhotos gets the result over OpenID Connect. Every common self-hosted provider can do this.

What this means for accounts: LibrePhotos matches people by the email address the directory holds, and only when the provider says it is verified (see How accounts are matched up). Either give each LibrePhotos account the same email as in the directory, or turn on sign-ups over SSO so that a first login creates the account.

The mobile app logs in with a username and password, not with SSO. A user who only ever signed in through the provider has no LibrePhotos password yet; they can set one with Forgot your password? on the login page (this needs email to be configured).

When something goes wrongโ€‹

A failed attempt returns to the login page with an explanation. The usual causes:

MessageWhat to change
Account not known to LibrePhotosCreate the account, or turn on OIDC_ALLOW_SIGNUP
Email address not verifiedVerify the address at your identity provider
More than one account uses this email addressGive each LibrePhotos account its own email address (or clear it on the ones that should not log in over SSO)
Sign-in was cancelled at your identity providerNothing to fix: the user pressed Cancel or declined access. Try again
Your identity provider could not complete the sign-inLook in the server log for the line starting SSO login via; it names the reason (for example an expired login, or the provider refusing the client secret)
Public URL not configuredSet FRONTEND_BASE_URL
Provider-side "invalid redirect URI"The redirect URI at your provider must match the callback URL above exactly, including the trailing slash