- PHP 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .forgejo/workflows | ||
| LICENSES | ||
| .pre-commit-config.yaml | ||
| OidcAuthMiddleware.php | ||
| README.md | ||
Grocy OIDC Middleware
This OidcAuthMiddleware enables OIDC/OAuth2 login for Grocy using the authorization code flow with PKCE. This middleware integrates with a Grocy instance version 4.7.0 or higher.
Note: This is not an official part of Grocy. Please report issues here and NOT in the Grocy repo.
Disclaimer: The middleware implementation was mainly created by GitHub Copilot (GPT-5.3-Codex).
This was tested against Pocket ID and should work with OAuth2/OIDC providers that support the authorization code flow.
OIDC auto-discovery is supported via issuer metadata.
Installation & Configuration
- Copy the
OidcAuthMiddleware.phpto the Grocy root atmiddleware/Auth/OidcAuthMiddleware.php - Configure a OAuth app in your OAuth provider
- Add the following settings to your
config.phpand adapt to your setup:
Setting('AUTH_CLASS', 'Grocy\Middleware\Auth\OidcAuthMiddleware');
// Options when using OidcAuthMiddleware
Setting('OAUTH_CLIENT_ID', '');
Setting('OAUTH_CLIENT_SECRET', '');
Setting('OAUTH_CLIENT_SECRET_FILE', ''); // Alternative when the secret is stored in a file
Setting('OAUTH_ISSUER', 'https://id.example.com');
Setting('OAUTH_REDIRECT_URI', 'https://grocy.example.com/stockoverview');
Setting('OAUTH_FLOW_COOKIE_SECRET', ''); // Use this or OAUTH_FLOW_COOKIE_SECRET_FILE; at least 32 characters
Setting('OAUTH_FLOW_COOKIE_SECRET_FILE', ''); // Alternative secret file; keep readable only by the webserver
// Transport and flow security
Setting('OAUTH_ALLOW_INSECURE_HTTP', false); // Development/debugging only
Setting('OAUTH_CLOCK_SKEW_SECONDS', 60);
// Identity and profile claims
Setting('OAUTH_PROMPT', 'select_account');
Setting('OAUTH_SCOPES', 'openid profile groups');
Setting('OAUTH_USERNAME_CLAIM', 'preferred_username');
Setting('OAUTH_FIRST_NAME_CLAIM', 'given_name');
Setting('OAUTH_LAST_NAME_CLAIM', 'family_name');
Setting('OAUTH_PICTURE_CLAIM', 'picture');
Setting('OAUTH_PICTURE_MAX_BYTES', 2097152);
Setting('OAUTH_PICTURE_ALLOWED_HOSTS', []); // Defaults to the OAUTH_ISSUER host
// Provisioning and group-based permissions
Setting('OAUTH_AUTO_PROVISION', false);
Setting('OAUTH_GROUPS_CLAIM', 'groups');
Setting('OAUTH_ADMIN_GROUP', 'grocy_admins');
With Pocket ID, a minimal setup is typically:
OAUTH_CLIENT_IDOAUTH_CLIENT_SECRETorOAUTH_CLIENT_SECRET_FILE(one is required)OAUTH_ISSUEROAUTH_REDIRECT_URIOAUTH_FLOW_COOKIE_SECRET
with the client configured to allow PKCE.
Generate a suitable flow-cookie secret on Linux with:
openssl rand -base64 32
Defaults and fallbacks
OAUTH_SCOPESfalls back toopenid profile groupsif not set.- The middleware supports confidential clients only; configure either
OAUTH_CLIENT_SECRETorOAUTH_CLIENT_SECRET_FILE. OAUTH_CLIENT_SECRET_FILEis used whenOAUTH_CLIENT_SECRETis empty.- Configure exactly one of
OAUTH_FLOW_COOKIE_SECRETandOAUTH_FLOW_COOKIE_SECRET_FILE; the secret must contain at least 32 characters. OAUTH_USERNAME_CLAIMfalls back topreferred_usernameand then triesusername,nickname,email,sub.OAUTH_FIRST_NAME_CLAIMfalls back togiven_name.OAUTH_LAST_NAME_CLAIMfalls back tofamily_name.OAUTH_PICTURE_CLAIMfalls back topicture.OAUTH_PICTURE_MAX_BYTESfalls back to2097152(2 MiB).OAUTH_PICTURE_ALLOWED_HOSTSdefaults to the hostname fromOAUTH_ISSUER; configure an explicit array to allow a different picture host.OAUTH_AUTO_PROVISIONdefaults tofalse; set it totrueto create a Grocy user when no matching username exists.OAUTH_GROUPS_CLAIMdefaults togroupsand identifies the UserInfo claim containing group memberships.OAUTH_ADMIN_GROUPis optional; users in this exact group receive Grocy'sADMINpermission. Users outside the group have only theADMINpermission removed.- Newly provisioned users outside the admin group receive all feature permissions and
MASTER_DATA_EDIT, but never user-management permissions orUSERS_EDIT_SELF. Later logins preserve any permission changes made by Grocy administrators. OAUTH_CLOCK_SKEW_SECONDSdefaults to60and controls the accepted clock difference for ID-token timestamps.OAUTH_ALLOW_INSECURE_HTTPdefaults tofalseand must only be enabled for local development or debugging. OAuth endpoints, discovery, JWKS, UserInfo, the redirect URI, and picture downloads require HTTPS otherwise.- PKCE with
S256is mandatory and is used together with the confidential-client secret, not as a replacement for it. OAUTH_SCOPESmust includeopenid; the middleware always validates the resulting ID token.OAUTH_PROMPTis always sent asprompton the authorization request and falls back toselect_accountif not set.
OIDC discovery behavior
- OIDC discovery is mandatory. The middleware fetches
{issuer}/.well-known/openid-configurationfromOAUTH_ISSUERand uses the authorization, token, and UserInfo endpoints returned by the document. - Discovery must advertise
client_secret_basicintoken_endpoint_auth_methods_supported, and itsissuermust exactly matchOAUTH_ISSUER.
IdP authorization controls
- Use
OAUTH_PROMPTto influence the IdP login behavior (login,consent,select_account, ornone, depending on your provider). - To prevent silent SSO re-login, keep
OAUTH_PROMPTatselect_account(recommended default).
Supported
- OAuth2 authorization code flow.
- Mandatory PKCE (
S256) with server-side verifier handling. - PKCE provider capability verification via discovery (
code_challenge_methods_supportedmust includeS256). - OIDC discovery (
/.well-known/openid-configuration) for auth/token/userinfo endpoints. - OIDC ID token validation (
RS256signature,iss,aud,exp,nonce, andsubmatch with userinfo). - User provisioning and profile synchronization (username/name/picture claim mapping).
- Prompt forwarding (
prompt) on every authorization request.
Not supported
- OIDC RP-initiated/back-channel/front-channel logout support.
- Refresh-token based session renewal.
- Complex claim transformations beyond direct claim-key mapping and username fallbacks.
- Non-
RS256ID token algorithms.
Redirect URI guidance
- Use one fixed redirect URI, configured via
OAUTH_REDIRECT_URI, and register that exact URI in your OAuth provider. - For Grocy, use a protected non-root, non-login page as callback (for example
/stockoverview). - Do not use
/or/loginas callback when using this middleware class.
Profile sync behavior
- On every successful login, Grocy user profile fields are synchronized from claims.
- User
first_nameandlast_nameare created from claims and updated on subsequent logins when claim values are present. - User profile pictures are downloaded from the configured picture claim URL and stored in Grocy
userpicturesstorage; unsupported mime types or failed downloads are ignored. - Profile-picture downloads require HTTPS, use the configured host allowlist, and do not follow redirects. The access token is not sent to the picture URL.
Username identity limitation
The middleware maps users by the configured username claim because it cannot add an identity mapping table to Grocy. Changing the IdP username can therefore create a new Grocy user or, if the new username matches another account, cause an account collision. Use an immutable username claim where possible and do not allow users to change it at the IdP.
Development checks
Install and enable the pre-commit hooks with:
python -m pip install pre-commit
pre-commit install
pre-commit run --all-files