Repository navigation
feat: Android support, symmetric families, family hand-over and Kakao login - #8
Merged
Merged
Conversation
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.
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.
S1 of docs/family-symmetric-plan.md. Server decisions no longer look at
users.role; the column stays for responses and for picking the analysis
subject until S3.
- One family per user. Anyone in a family can create or resend an
invitation. Anyone with no family, or alone in their own family, can
accept (the solo family is removed); someone already in a family with
other people gets 409.
- POST /v1/families returns the caller's family or creates it.
- Member list adds acceptedAt, consentGranted and isCreator, lists the
creator in the same shape (empty relation), sets canInvite for
everyone and shows invitations to every member.
- Calls work between any two members of the same family. calls.parent_id
is the analysis subject: the one PARENT if exactly one participant has
that role (current iOS behaviour), otherwise the callee.
- /parents/{id}/... reads id as the subject user: the subject and
same-family members may read it; same-family members may also edit
the profile once the subject has consented.
- Google login no longer needs a role. Without one the account starts
with no family; clients that send a role (iOS) keep the old flow.
S2 of docs/family-symmetric-plan.md.
- Migration 0013 adds users.nickname (unique), gender, birth_date and
email, all nullable for existing accounts.
- UserView gains the same fields plus signupCompleted (has a nickname).
- GET /v1/account/nickname-availability returns {available, reason}
with reason INVALID or TAKEN. Nicknames are 2-10 Hangul syllables,
Latin letters or digits.
- PATCH /v1/account/profile sets all three fields; a taken nickname is
409 and a malformed one 422.
- Google login stores the verified email from the ID token.
- The member list shows each person's nickname and birth date.
S4 of docs/family-symmetric-plan.md.
- Migration 0014 adds family_shares (owner_id, viewer_id, report,
timeline) and users.marketing_opt_in (default false).
- GET/PUT /v1/account/sharing lists one row per connected member of the
caller's family; with no row everything is shared. Reports, baselines
and signals check `report`; the call list checks `timeline`. Profiles
and daily questions stay open to the family.
- DELETE /v1/families/{familyId}/members/{memberId}: any member can drop
a pending invitation; a connected member can be removed by the family
creator or leave on their own, which leaves them without a family and
clears the sharing rows between them and the rest of the family. The
creator cannot be removed (409).
- The member list gains canRemove.
- PATCH /v1/account/preferences sets marketingOptIn; UserView shows it.
- Account deletion also removes the user's sharing rows.
The family creator showed up to others by their Google/Apple account name. Members list, family list, incoming call push and LiveKit names now prefer the sign-up nickname and fall back to the account name, so accounts without a nickname (iOS) look the same as before.
The creator could not leave (409) and deleting their account deleted the whole family, cutting off everyone else. Now the creator can leave by removing themselves, and both leaving and account deletion pass the family to the member who was invited first; a family with nobody left is deleted with its pending invitations, as before.
Mirror the Google flow: the app asks for a single-use nonce, signs in with the Kakao SDK, and the server verifies the ID token against Kakao's JWKS (issuer, audience = KAKAO_APP_KEY, nonce). New accounts are role-free and get a family on their first invitation. The shared challenge and OIDC verification code now serves both providers.
The web client ID is public (it is the audience of Android ID tokens), so the production compose file now falls back to the Collog Firebase web client when backend.env does not set GOOGLE_CLIENT_ID. Android sign-in works on the existing production env without a host change.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ships the Android client's server work in one deploy. Includes #6 and #7 (they close when this merges), plus:
feat/family-ownership: when the family creator leaves or deletes their account, the earliest joined member becomes the owner (the family is deleted only when nobody is left)feat/kakao-login: Kakao login via OpenID Connect ID tokens (/v1/auth/kakao/challenge,/v1/auth/kakao), migration 0015. Disabled untilKAKAO_APP_KEYis setGOOGLE_CLIENT_IDto the Collog Firebase web client (public value), so Android Google sign-in works without a host changeDeploy notes
2026-10-26.v5, so existing iOS users consent once more.FCM_KEY_FILEin/etc/collog/backend.envplus the Firebase key file on the host. Without it the server starts normally and Android devices simply get no push.Tests: 339 passed, ruff clean.