Skip to content

Authentication and authorization

GeoSync is a browser-session application. It has three selectable modes: central (current checked-in configuration), local (break-glass), and keycloak (legacy OIDC). There is no implemented public API-key scheme and no GeoSync JWT bearer contract. The bearer token used during OIDC callback is an internal server-to-Keycloak userinfo call, not client authentication to GeoSync.

Mode selection

geosync_auth_mode() chooses, in order:

  1. non-empty GEOSYNC_AUTH_MODE;
  2. central if CENTRAL_DATABASE_URL or CENTRAL_LOGIN_URL is set;
  3. central if the request carries a non-empty acugis_session cookie;
  4. config/auth.php's mode;
  5. local.

The checked-in config/auth.php selects central. Deployment values may be loaded from deploy/geosync-auth.env or /etc/geosync-auth.env without overwriting already-set environment variables. GEOSYNC_BASE_PATH may override automatic /geosync-service or /geosync mount detection.

Central session mode

The browser sends raw cookie acugis_session. GeoSync hashes it as lowercase SHA-256 and looks up dashboard_sessions.token_hash, requiring expires_at > now() and an enabled dashboard_users row. The join is by username. Group names come from auth_group_members/auth_groups, falling back to the user's JSON groups. If direct Central DB resolution is unavailable or fails, GeoSync tries authenticated GET /api/me (configured CENTRAL_API_ME_URL, loopback :8888, then same-host /api/me).

Protected Central-mode requests:

  1. resolve the Central user;
  2. redirect unauthenticated users to CENTRAL_LOGIN_URL (default /login) with a URL-encoded next;
  3. require platform administrator status;
  4. hydrate the legacy PHP session key qz_user with a synthetic Admin object whose ID is SUPER_ADMIN_ID.

An administrator is either dashboard_users.is_admin, or belongs case-insensitively to administrator, administrators, admin, admins, or dashboard-admin. Non-admin denial is HTTP 403 (JSON for /api/ or JSON Accept, otherwise HTML). Central store failure is HTTP 503.

Central logout clears the PHP session and returns HTTP 303 to CENTRAL_LOGOUT_URL (default /logout). Central login similarly redirects with HTTP 303.

The repository also contains generic resource permission functions. Valid permissions are view, edit, and admin; admin satisfies every check and edit satisfies view. Administrator groups bypass resource checks, and the Public group is implicitly included. The installed Phase 1A SQL grants admin for service geosync only to administrators. The active prepend gate, however, directly requires Central platform admin and does not call the resource-level check.

Local mode

POST /admin/action/login.php accepts form fields submit, email, and pwd. It loads public.user by email and applies password_verify, then stores the row under PHP session key qz_user. Success and failure are browser redirects, not JSON:

  • success: Location: ../../index.php;
  • failure: Location: ../../login.php?err=....

Administrative action scripts generally require qz_user.accesslevel == 'Admin'; read pages may require only a session or enforce ownership. Ownership is based on owner_id, sharing constants, and the special SUPER_ADMIN_ID. There is no CSRF token in the reviewed form action handlers; the PHP session cookie is therefore the critical credential and callers should use same-origin controls and HTTPS.

Public signup/verification files remain present but geosync_registration_disabled.php blocks them before handler logic.

Legacy Keycloak OIDC

GET /auth/oidc/login.php creates a 16-byte random state, stores its hex value in the session, and redirects to the issuer authorization endpoint with authorization-code flow, scopes openid profile email, client ID, exact redirect URI, and state.

GET /auth/oidc/callback.php?code=...&state=...:

  • requires and constant-time validates one-use state;
  • exchanges the code at the Keycloak token endpoint using the configured client secret;
  • calls userinfo with the returned access token;
  • extracts realm roles plus roles under resource_access[client_id];
  • optionally requires any role listed in required_roles_any;
  • stores kc_sub, email, name, and normalized roles in $_SESSION['auth'];
  • maps role admin or geosync_admin to legacy Admin, all others to User, and stores a compatibility qz_user object;
  • redirects to post_login_redirect.

The implementation decodes access-token claims to read roles but does not locally verify the JWT signature/issuer/audience; successful token exchange and userinfo are relied upon. Do not expose the checked-in OIDC client secret; it must be rotated and supplied through deployment secret management. This documentation intentionally omits all secret values.

Legacy logout clears the PHP session and redirects to Keycloak's end-session endpoint with client ID, an absolute post-logout URI, and id_token_hint when available.

Google, GitHub, and Microsoft provider OAuth files

Top-level auth-google.php, auth-github.php, and auth-microsoft.php are additional legacy OAuth authorization-code callbacks. Their configuration is via constants in admin/incl/const.php; account auto-creation is governed by DISABLE_OAUTH_USER_CREATION. Each uses OAuth state, looks up a local user by provider email, and otherwise can create a local User.

These files contain placeholder qcarta redirect/viewer paths and are not selected by geosync_auth_mode(). Treat them as legacy/incomplete integration, not a supported API login contract.

Cookies, headers, and proxy identity

  • Client authentication is a session cookie: acugis_session in Central mode and the configured PHP session cookie containing qz_user in local/OIDC compatibility mode.
  • /api/proxy.php accepts any of user_id, qz_user, or OIDC kc_sub session indicators. It returns HTTP 401 text when none exists.
  • When proxying an allowed operation, it sets upstream Content-Type: application/json and X-User-ID to the resolved legacy session user ID (possibly empty for a pure OIDC session). This header is generated by GeoSync; clients must not rely on supplying it themselves.
  • The proxy forwards upstream status only when it is 400 or greater. Many legacy admin actions return HTTP 200 with {"success":false,...} and do not set JSON content type. Always evaluate the JSON success member.
  • No API key header, HTTP Basic API contract, Authorization: Bearer handling, refresh-token endpoint, or machine-to-machine grant was found.

Public-path exceptions

Central prepend excludes /login.php, /logout.php, /signup.php, /admin/action/login.php, /admin/action/signup.php, and /admin/action/verify.php. “Public” here means bypassing the Central prepend; signup/verify are separately disabled, and local login still validates credentials. OIDC endpoints are exempted by the older auth_gate.php when that deployment mechanism is used.