Skip to content

Sign-in and OAuth

People can sign in to your server in three ways, side by side:

  • Email and password, the default.
  • Your own identity provider through OAuth (OpenID Connect), such as Authentik, Authelia, Keycloak, Okta or Google.
  • Sign in with Frameleaf, once your server is linked to Frameleaf Cloud. See Frameleaf Cloud account.

All three are set in Settings, then Access & security. Your own provider and password login are under Sign-in methods.

Password Login, then Login with email and password, turns password sign-in on or off for the whole server. When it’s off, nobody can sign in with a password, including administrators. If OAuth is off as well, nobody can sign in at all, so you’re asked to confirm before saving. Changing it doesn’t sign out anyone already signed in.

The details vary by provider, but the shape is the same:

  1. Create a new application or client.
    • Provider type: OpenID Connect or OAuth2
    • Client type: Confidential
    • Application type: Web
    • Grant type: Authorization Code
  2. Add the redirect URIs listed below.
  3. If the provider supports it, set the back-channel logout URL to https://photos.example.com/api/oauth/backchannel-logout.
  4. Copy the client ID and client secret.

Add these for every address you use to reach the server, such as http://localhost:2283, http://192.168.0.200:2283 and https://photos.example.com:

Redirect URI What it’s for
https://photos.example.com/auth/login Signing in on the web
https://photos.example.com/user-settings Linking an existing account to the provider on the web
frameleaf-auth:///oauth-callback Signing in from the Frameleaf app
app.immich:///oauth-callback Signing in from the older mobile app, if people still use it

Settings, then Access & security, then Sign-in methods lists the exact app callbacks for your configuration under Mobile app callbacks. Each app signs in separately; signing in to one never signs you in to the other. See Moving from Immich.

Open Settings, then Access & security, then Sign-in methods. Under OAuth, turn it on and fill in the settings below. For the client secret, select Replace credential; it’s stored write-only and only ever shows Stored or Not set.

Setting What it does Default
issuer_url Your provider’s discovery address. /.well-known/openid-configuration is added automatically if you leave it off Required
client_id From your provider Required
Client secret From your provider Required
token_endpoint_auth_method How the client authenticates to the provider client_secret_post
scope Scopes to request, separated by spaces openid email profile
id_token_signed_response_alg Algorithm the ID token is signed with, such as RS256. Leave it empty to accept what the provider advertises RS256
userinfo_signed_response_alg Algorithm the userinfo response is signed with none
prompt Sent to the provider, for example select_account, login or consent Empty
end_session_endpoint Another sign-out address at your provider Empty
Request Timeout Milliseconds to wait for the provider 30000
Allow insecure requests Turns off certificate checks for OAuth requests. Only for testing; it exposes you to interception Off
Storage label claim Claim used for the person’s storage label preferred_username
Role Claim Claim that makes someone an administrator. It should be user or admin immich_role
Storage quota claim Claim holding the person’s quota in GiB immich_quota
Default storage quota (GiB) Quota for people without a quota claim. Leave empty for unlimited Empty
Button text Text on the sign-in button Login with OAuth
Account Management URL Where people manage their profile at your provider Empty
Auto register Create an account the first time someone signs in On
Auto launch Skip the sign-in page and go straight to the provider Off
Mobile redirect URI override See below Off

The storage label, role and quota claims are only read when an account is created. They aren’t kept in sync afterwards. The claim names immich_role and immich_quota are the real defaults, kept so existing provider setups work.

Then select Save changes.

An email address from your provider is used to link a sign-in to an existing account, or to create a new account, only when the provider says it’s verified: the email_verified claim must be true. A sign-in with an unverified address, or without the claim, is refused with a message saying why.

Some providers, such as Microsoft Entra ID, don’t send email_verified. With them, people whose accounts aren’t linked yet can’t sign in by email, and automatic registration is refused, until you map an email_verified claim at the provider. Accounts already linked by their provider ID keep signing in as before.

A sign-in only finishes in the browser or app that started it, within the provider’s time limit. Otherwise nobody is signed in and the sign-in page shows one of these:

Message Why
The identity provider did not approve the sign-in The person declined, or the provider refused
This sign-in was started somewhere else or has expired The callback belongs to a different sign-in
This sign-in link has expired or was already used The code was used already, expired or didn’t match

The provider’s own error text is never shown. A callback that opens in another tab of the same browser, for example from an email link, still finishes for 15 minutes.

With Auto launch on, the sign-in page goes straight to your provider. To reach the normal sign-in page, use the browser’s back button or open /auth/login?autoLaunch=0. You can also auto launch for a single visit with /auth/login?autoLaunch=1, which is handy when Frameleaf is embedded in another site that already signs people in.

If your provider won’t accept an app address such as frameleaf-auth:///oauth-callback:

  1. Turn on Mobile redirect URI override.
  2. Set Mobile redirect URI to your server’s /api/oauth/mobile-redirect address, for example https://photos.example.com/api/oauth/mobile-redirect. Frameleaf already forwards this address to the older mobile app.
  3. With your provider, allow that address and the same address ending in /api/oauth/frameleaf-mobile-redirect, for example https://photos.example.com/api/oauth/frameleaf-mobile-redirect. The Frameleaf app is sent there.

The override must be your server’s own /api/oauth/mobile-redirect address. Any other address can’t be matched for the Frameleaf app, and its sign-in is refused with an error.

These show the Frameleaf side of a working setup. Replace photos.example.com and the provider address with your own.

In the provider’s Protocol settings, set Client type to Confidential, add your redirect URIs one per line, and copy the client ID and secret.

Setting Value
issuer_url https://authentik.example.com/application/o/frameleaf/
scope openid email profile
id_token_signed_response_alg RS256
Storage label claim preferred_username
Storage quota claim immich_quota
Button text Sign in with Authentik (optional)

This client also passes an optional immich_quota claim from an LDAP attribute called frameleafquota:

authentication_backend:
ldap:
attributes:
extra:
frameleafquota:
name: 'immich_quota'
multi_valued: false
value_type: 'integer'
identity_providers:
oidc:
claims_policies:
frameleaf_policy:
custom_claims:
immich_quota:
attribute: 'immich_quota'
scopes:
frameleaf_scope:
claims:
- 'immich_quota'
clients:
- client_id: 'frameleaf'
client_name: 'Frameleaf'
client_secret: '<hashed client secret>'
public: false
require_pkce: true
pkce_challenge_method: 'S256'
redirect_uris:
- 'https://photos.example.com/auth/login'
- 'https://photos.example.com/user-settings'
- 'frameleaf-auth:///oauth-callback'
scopes: ['openid', 'profile', 'email', 'frameleaf_scope']
claims_policy: 'frameleaf_policy'
response_types: ['code']
grant_types: ['authorization_code']
id_token_signed_response_alg: 'RS256'
userinfo_signed_response_alg: 'RS256'
token_endpoint_auth_method: 'client_secret_post'
Setting Value
issuer_url https://auth.example.com
client_id frameleaf
token_endpoint_auth_method client_secret_post
scope openid email profile frameleaf_scope
id_token_signed_response_alg RS256
userinfo_signed_response_alg RS256
end_session_endpoint https://auth.example.com/logout?rd=https://photos.example.com/
Storage label claim uid
Storage quota claim immich_quota
  1. Create an OpenID Connect client in your realm and set its Client ID, for example frameleaf.
  2. Under Access settings, set Root URL, Home URL and Admin URL to your server address. Add your redirect URIs to Valid redirect URIs, and set Valid post logout redirect URIs and Web origins to +.
  3. Turn on Client authentication so the client is confidential, and copy the secret from Clients, then your client, then Credentials.
  4. To manage roles, create a client role mapper with the claim name immich_role.
Setting Value
issuer_url https://<keycloak-domain>/realms/<your-realm>
client_id frameleaf
scope openid email profile
id_token_signed_response_alg RS256
Role Claim immich_role
Button text Sign in with Keycloak

In the OAuth client’s Authorised redirect URIs, add https://photos.example.com/auth/login, https://photos.example.com/user-settings and https://photos.example.com/api/oauth/mobile-redirect. Google says changes can take from five minutes to a few hours to apply. Google needs the mobile redirect override, so also add https://photos.example.com/api/oauth/frameleaf-mobile-redirect.

Setting Value
issuer_url https://accounts.google.com
scope openid email profile
id_token_signed_response_alg RS256
Mobile redirect URI override On
Mobile redirect URI https://photos.example.com/api/oauth/mobile-redirect