Skip to content

feat: add Google sign-in and FCM push for the Android client - #6

Open
Frekion2002 wants to merge 11 commits into
mainfrom
feat/android-port
Open

Frekion2002 wants to merge 11 commits into
mainfrom
feat/android-port

Conversation

@Frekion2002

Copy link
Copy Markdown
Collaborator

Add the server side of the Android client (Collog-App/Collog-Android).

Google sign-in: /v1/auth/google/challenge and /v1/auth/google follow the Apple flow. The server issues a one-time nonce, and the ID token is checked against Google's JWKS (both issuers, aud = GOOGLE_CLIENT_ID, nonce). Accounts are found by the new users.google_subject. Someone who signs up with Apple on iOS and with Google on Android gets two accounts; they are not merged. Google accounts are deleted without re-authentication, like OTP accounts.

FCM: Android devices register with platform=ANDROID and one FCM token for token, voipToken and pushToken. Incoming calls and report notifications go out as FCM HTTP v1 data messages signed with a Firebase service account. The payload has the same fields as the APNs push, with no health data and no LiveKit credentials. A stored token is cleared only on UNREGISTERED, or INVALID_ARGUMENT that names message.token, and only on that device row.

Deploying with both settings empty is safe. The Google endpoints return 503, Android devices do not count as call receivers, and report notifications skip them. iOS behaves as before, except that a report retry within the same process no longer resends to devices that already received it.

Merging runs migration 0011, which only adds: a nullable users.google_subject with a unique index, and a google_login_challenges table.

To turn Android on in production, set GOOGLE_CLIENT_ID, put the service account JSON on the host with a read-only bind like the APNs key, and set FCM_SERVICE_ACCOUNT_PATH to the path inside the container. The compose files do not have that bind yet.

uv run pytest: 304 passed. ruff check: clean.

Mirror the Apple login flow: a one-time server nonce challenge, Google ID
token verification against Google's JWKS (both issuer spellings, audience =
Web OAuth client ID), and users.google_subject. GOOGLE_CLIENT_ID left empty
keeps both endpoints at 503. Migration 0011 adds the column and the
google_login_challenges table.
Devices registered with platform=ANDROID now receive the incoming-call and
report-ready signals as FCM HTTP v1 data messages. The access token is
minted from a Firebase service account (FCM_SERVICE_ACCOUNT_PATH) and
reused; unset keeps Android delivery disabled. iOS delivery is unchanged.
- An empty FCM_SERVICE_ACCOUNT_PATH (compose default) means unset instead of
  Path('.') crashing the whole backend on start.
- Without FCM, Android rows are left out of report delivery, and report
  retries skip devices that already received the notification.
- The callee receiver check accepts Android devices when FCM is configured.
- Only UNREGISTERED or an INVALID_ARGUMENT naming message.token counts as a
  dead token, and clearing it touches that device row only.
- A 401 drops the cached access token; token_uri must be Google's.
- Report messages collapse per call; service-account JSON is git-ignored.
The compose files passed FCM_SERVICE_ACCOUNT_PATH through as a raw path but
mounted no key file, so a host path in the deploy env made AppContainer fail
at startup and took the API down for every client. Mount FCM_KEY_FILE like the
APNs key and fill the container path only when it is set; when it is empty,
/dev/null is mounted and Android push stays off.
Android also requests question audio from ElevenLabs directly and signs in
with Google, so the iPhone-only wording was not true for Android users. Say
phone instead of iPhone, name both login tokens, and bump the document
version so the change is recorded as a new consent.
pyjwt 2.15.1, aiohttp 3.14.5, urllib3 2.8.0, certifi 2026.10.23 and
multidict 6.9.1 (kept on 6.x). pip-audit reports none afterwards.
@Frekion2002

Copy link
Copy Markdown
Collaborator Author

Local end-to-end test and follow-up commits (2026-10-26)

Tested locally (this branch + local LiveKit + Android emulator and an Android phone on the same Wi-Fi):
Google sign-in, device registration, FCM incoming call, accept, two-way audio (checked by ear), hang-up ending both sides. A release (R8) build of the app was also run against this branch: sign-in, home data, incoming call with notification permission revoked, accept, LiveKit connect, remote end, logout.

Commits added since 8a07f41 (CI green on 2701435):

Commit Why
ccd981e fix: mount the FCM service account key in compose The compose files passed FCM_SERVICE_ACCOUNT_PATH through but mounted no file, so a host path in the deploy env made AppContainer fail at startup and took the API down for every client. Now set FCM_KEY_FILE (host path) like APNS_KEY_FILE; empty mounts /dev/null and leaves Android push off.
5b036fe fix: describe both phone platforms in the consent document Android also fetches question audio from ElevenLabs directly and signs in with Google, so "아이폰"/"Apple 로그인 토큰" was not true for Android users. Bumps the document to 2026-10-26.v5.
db5bb40 fix: allow clock skew when verifying Apple identity tokens Same 60 s leeway as Google.
b7f0e44 build: upgrade dependencies with known vulnerabilities pyjwt 2.15.1, aiohttp 3.14.5, urllib3 2.8.0, certifi 2026.10.23, multidict 6.9.1. pip-audit reports none afterwards.
48697d3 docs HANDOFF updated.
2701435 fix: keep the consent version constant in step with the settings app/consent.py default was still v4.

Please note when merging/deploying

  • The consent version bump means existing users must agree again before recording and AI analysis turn back on (calls keep working). If you would rather keep v4 for now, drop 5b036fe and 2701435 and keep the wording for later.
  • Production env: use FCM_KEY_FILE=<absolute host path to the Firebase service account JSON>. FCM_SERVICE_ACCOUNT_PATH is now set by compose and ignored from the env file.

The final Figma asks for 건망증 · 호흡기 질환 · 비염 plus a free-text
"추가적으로 걱정되는 부분". Health profiles now accept FORGETFULNESS,
RESPIRATORY and RHINITIS alongside the earlier codes (the iOS app still
sends those) and store an optional `concerns` note of up to 500 chars.

- Migration 0012 adds parent_profiles.concerns.
- PUT keeps the stored note when a client sends only conditions.
- Daily question pools for the three new conditions use open
  questions, as docs/profile-question-report-design.md §5 asks.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant