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
Section titled “Password login”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.
Set up your own provider
Section titled “Set up your own provider”1. Create a client at your provider
Section titled “1. Create a client at your provider”The details vary by provider, but the shape is the same:
- Create a new application or client.
- Provider type: OpenID Connect or OAuth2
- Client type: Confidential
- Application type: Web
- Grant type: Authorization Code
- Add the redirect URIs listed below.
- If the provider supports it, set the back-channel logout URL to
https://photos.example.com/api/oauth/backchannel-logout. - Copy the client ID and client secret.
Redirect URIs
Section titled “Redirect URIs”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.
2. Turn on OAuth in Frameleaf
Section titled “2. Turn on OAuth in Frameleaf”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.
Verified email addresses
Section titled “Verified email addresses”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.
Refused and expired sign-ins
Section titled “Refused and expired sign-ins”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.
Auto launch
Section titled “Auto launch”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.
Mobile redirect override
Section titled “Mobile redirect override”If your provider won’t accept an app address such as frameleaf-auth:///oauth-callback:
- Turn on Mobile redirect URI override.
- Set Mobile redirect URI to your server’s
/api/oauth/mobile-redirectaddress, for examplehttps://photos.example.com/api/oauth/mobile-redirect. Frameleaf already forwards this address to the older mobile app. - With your provider, allow that address and the same address ending in
/api/oauth/frameleaf-mobile-redirect, for examplehttps://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.
Examples
Section titled “Examples”These show the Frameleaf side of a working setup. Replace photos.example.com and the provider address with your own.
Authentik
Section titled “Authentik”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) |
Authelia
Section titled “Authelia”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 |
Keycloak
Section titled “Keycloak”- Create an OpenID Connect client in your realm and set its Client ID, for example
frameleaf. - 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
+. - Turn on Client authentication so the client is confidential, and copy the secret from Clients, then your client, then Credentials.
- 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 |