The app offers Google and Apple sign-in for hosts. The Apple button ships in the code and the backend knows the provider; it works the moment the two values below exist on the Convex deployment. Everything on this page happens in Karim's Apple developer account and on Karim's machine — it involves a private key, so nobody else does it.
scripts/, and set
AUTH_APPLE_ID + AUTH_APPLE_SECRET on Convex. The secret expires every
six months — put a reminder in the calendar.
AUTH_APPLE_ID and AUTH_APPLE_SECRET are both set on the dev and production
deployments. The values:
| Team ID | AL7Y7C2959 |
| Services ID | com.karimdev.gamesforgroup.signin (domains + return URLs registered for dev upbeat-scorpion-654 and prod clean-poodle-980) |
| Key ID | TZXQXFP2MM — keep the .p8 file in a password manager; Apple never lets you download it again |
RENEWALS.md at the repo root.
| Value | Where it comes from | Goes to |
|---|---|---|
| Team ID | Apple developer account → Membership | the secret script |
Services ID (e.g. com.karimdev.gamesforgroup.signin) | Identifiers → Services IDs | AUTH_APPLE_ID and the script |
Key ID + the .p8 file | Keys → the Sign in with Apple key | the script (never Convex, never git) |
| Client secret (a signed token) | the script's output | AUTH_APPLE_SECRET |
com.karimdev.gamesforgroup (the app's bundle id from app.json).com.karimdev.gamesforgroup.signin. Continue → Register.com.karimdev.gamesforgroup.upbeat-scorpion-654.convex.site; add the production deployment's <name>.convex.site too
(it is the "HTTP Actions URL" on the Convex dashboard, without https://).https://<deployment>.convex.site/api/auth/callback/apple, one line per deployment.Apple only accepts https return URLs on real domains — that is why the callback lives on the Convex site, not on the phone. The app finishes the round-trip on its own scheme afterwards, exactly as it does for Google.
com.karimdev.gamesforgroup → Save → Continue → Register..p8 file. Apple lets you download it once. Keep it somewhere private (a password manager's file vault). Note the Key ID shown on that page.Apple does not give you a client secret; you sign one yourself with the key. The repo has a script for it (no dependencies, Node only). Run from the repo root:
node scripts/apple-client-secret.mjs \
--team <TEAM_ID> \
--key-id <KEY_ID> \
--client-id com.karimdev.gamesforgroup.signin \
--key ~/path/to/AuthKey_<KEY_ID>.p8
It prints one long token. That token is AUTH_APPLE_SECRET. It is valid for six months (Apple's maximum) — the script prints the expiry date; rerun it and reset the variable before then.
Once for dev, once for production (add --prod):
bunx convex env set AUTH_APPLE_ID com.karimdev.gamesforgroup.signin
bunx convex env set AUTH_APPLE_SECRET <the token from step 4>
No deploy is needed after this — the provider reads the values on each sign-in.
| You see | Usually means |
|---|---|
| "Sign-in did not complete" toast straight away | the two env values are missing or the secret expired — bunx convex env list |
| Apple page: invalid_client | Services ID mismatch — AUTH_APPLE_ID must be the Services ID, not the App ID |
| Apple page: invalid_request · redirect_uri | the Return URL on the Services ID does not match https://<deployment>.convex.site/api/auth/callback/apple exactly |
| Signed in but no name | expected after the first sign-in — Apple never sends it again; the host types it once and the app remembers |
| Apple accepts the password, the app shows "something went sideways" | the users insert failed — check bunx convex logs. Seen once for image: null (fixed 7 Sep by mapping the profile by hand); the same shape of failure would come from any new field Apple sends as null |
Why Apple at all: App Store guideline 4.8 — an app that offers any third-party sign-in must also offer Sign in with Apple. The rule is enforced at review, so the button shipped with the Google one (see decisions.html, 2026-09-06).