TimetableBlue Drop Labs

Google sign-in setup

Timetable supports Google’s authorization-code sign-in flow. Password login and existing invitations remain available. Google identities attach to the shared account, so the same connection works across all of a person’s teams.

Configure Google Cloud

In your Google Cloud project, configure the Google Auth Platform branding and audience for Timetable by Blue Drop Labs. Create an OAuth client with application type Web application.

Register this exact production authorized redirect URI:

https://timetable.bluedroplabs.com/api/auth/google/callback

For local testing, register a separate development client with:

http://localhost:4321/api/auth/google/callback

The callback uses the frontend origin so the browser keeps its Timetable session cookie while Cloudflare proxies the callback to Symfony. Do not use the Tailscale API hostname as the callback for the Cloudflare website.

Request only openid and email. No Gmail, Drive, calendar, refresh-token, or offline access is needed. While the Google OAuth app is in testing, add the intended people as test users. If using an internal Google Workspace audience, only that Workspace’s accounts can authorize it; choose an external audience to support people from other companies.

Use Google’s current OpenID Connect setup guide for the console steps and consent-screen requirements.

Configure the backend

Set these values in the Timetable backend deployment environment. Keep the client secret out of frontend assets and chat.

GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-client-secret
GOOGLE_REDIRECT_URI=https://timetable.bluedroplabs.com/api/auth/google/callback

Redeploy the backend after changing its environment.

For DDEV, put development credentials in .env.local in the Symfony repository. Public GET /api/auth/providers reports whether all three values are present. With any missing, Google sign-in is disabled and the password form stays available.

Connect an existing account

  1. Sign in to Timetable with your existing password.
  2. Open Settings → Google sign-in.
  3. Confirm your Timetable password and choose Connect Google account.
  4. Choose the Google account you want to connect.
  5. After the success message, sign out and use Sign in with Google.

The Google email may differ from your Timetable email because connecting requires proof of both accounts. Google does not overwrite your Timetable name, email, timezone, roles, or memberships. Timetable keys the connection by Google’s stable sub, not by email. It never silently merges accounts with matching email addresses. A first-time Google sign-in creates a private team with an admin account. If that email already belongs to a Timetable account, sign in with its password and connect Google in Settings. New invitees currently accept their invitation with a password, then connect Google in Settings.

Disconnecting requires the current Timetable password. Existing password accounts can still sign in with their password afterward. A new Google-only account has no usable password and cannot disconnect Google through Settings. Deactivated team memberships remain inaccessible; if all memberships are inactive, Google login fails too.

Implementation and validation

The backend uses Google’s PHP client to verify the signed ID token against Google’s rotating keys. It additionally checks audience, authorized party when present, expiry, a one-time nonce, verified email, and subject. Authorization uses session-bound state, a ten-minute expiry, PKCE S256, and a single-use callback. Linking also verifies that the initiating account and password have not changed. Successful authentication uses Symfony’s session login and CSRF rotation. Connecting accounts requires the browser session, CSRF protection, and the existing password; API tokens cannot manage the connection.

Only the Google subject, verified email, link UUID, account UUID, and link timestamp are persisted. OAuth codes and provider access/refresh/ID tokens are never stored or included in user-facing errors.

Backend tests use signed test JWTs and a mocked Google HTTP boundary to test both verification and rejection. Controller tests cover linking, login, replay, cancellation, unknown accounts, identity conflicts, inactive memberships, password checks, and API-token restrictions. A real Google round trip must still be tested after production credentials and the consent screen are configured.

Production Apache access logs omit query strings and referrers. Cloudflare observability redacts request query strings so OAuth callback codes do not enter those logs. Avoid recording OAuth callback traffic with an external debug proxy.