Google (sign-in and Meet)
The backend uses Google for two unrelated things with two different OAuth clients. Mixing them up is the most common setup mistake.
| Sign-in (app users) | Meet (staff, online events) | |
|---|---|---|
| Who | Men using the mobile app, web app or website | Staff organisers in the Admin CRM |
| Consent screen | External | Internal (Workspace only) |
| Clients | Web, iOS, Android (one Android client per signing certificate) | One web client |
| Env | GOOGLE_SIGNIN_CLIENT_IDS (comma-separated) | GOOGLE_OAUTH_CLIENT_ID, GOOGLE_OAUTH_CLIENT_SECRET, GOOGLE_OAUTH_REDIRECT_URIS, GOOGLE_TOKEN_KEY |
| Code | EMAW/social_auth.py, EMAW/google_identity.py, model SocialIdentity | events/google_meet.py, events/google_views.py, models GoogleAccount, MeetSpace, MeetParticipant |
| Endpoints | POST /rest/auth/google/, POST/DELETE /rest/auth/google/link/, GET /rest/auth/identities/ | /rest/events/staff/google/, …/google/connect/, …/google/callback/ |
| Runbook | docs/GOOGLE_SIGN_IN_SETUP.md | events/README.md ("Recurring events, leaders, Google Meet") |
Sign-in
The client obtains a Google ID token on the device or in the browser (Google Identity Services on the web; @react-native-google-signin/google-signin on mobile) and posts it to /rest/auth/google/. The backend verifies the token's audience against GOOGLE_SIGNIN_CLIENT_IDS, then signs in or creates the account, auto-linking by hashed email. An unverified account is activated and its password cleared. With no client ids configured, Google sign-in is off and the clients hide their buttons.
Each frontend origin must be an Authorized JavaScript origin on the web client. Android needs no client id in the app: it is matched by package name and SHA-1, so register the debug keystore's SHA-1 and each EAS build credential's SHA-1 as separate Android clients. A native rebuild (not an OTA update) is needed when the mobile client ids change.
Meet
Staff connect their own Workspace Google account once (google/connect/ opens the OAuth popup; /google/callback in the admin app lands it). Tokens are encrypted with GOOGLE_TOKEN_KEY and stored in GoogleAccount. For an online event the backend then creates a Meet space, calendar invites and recordings as the organiser, and fetch_recent_meet_attendance (beat, every 30 min) pulls attendance into MeetParticipant.
GOOGLE_TOKEN_KEY defaults to a derivation of SECRET_KEY: rotating SECRET_KEY without setting an explicit token key means every staff member must reconnect Google. With the client unset, Meet features are off.