7.9 KiB
Supabase auth setup: Google OAuth, Apple native sign-in (iOS), fueltide.io email
This doc lists the user-side steps required to make the code changes in this branch functional. Nothing here is performed by Nix — these are manual actions on external services, LDAP, SOPS, and DNS.
The Nix changes in this branch cover:
hosts/web-arm/modules/supabase/default.nix— GoTrue env for Google OAuth (web code-exchange flow) and Apple native sign-in (iOS id_token flow,GOTRUE_EXTERNAL_APPLE_CLIENT_ID=io.fueltide.workout), SMTP pointed atmail.cloonar.com:587,MAILER_AUTOCONFIRM=false,SITE_URL+URI_ALLOW_LISTfor fueltide.io.hosts/web-arm/modules/supabase/env-generate.sh— newauth.envblock that pulls SMTP + Google creds from SOPS.hosts/mail/modules/dkim-fueltide.nix— installs a per-domain DKIM key for fueltide.io into rspamd so outbound mail fromnoreply@fueltide.iois signed.
Apple sign-in is scoped to the native iOS flow only: the app uses
AuthenticationServices to obtain an Apple id_token, then calls
supabase.auth.signInWithIdToken({ provider: 'apple', token, nonce }).
GoTrue verifies the id_token against Apple's JWKS and checks that aud
matches io.fueltide.workout. No server-side client secret, .p8 key, or
Services ID is needed. Android uses native Google sign-in (handled
separately) and no Apple browser flow is supported.
Complete the six steps below before merging to master. Merging without them will deploy a broken GoTrue (missing Google/SMTP creds → auth emails fail, Google OAuth flows 500).
1. LDAP service account + fueltide alias on mail.cloonar.com
Mirrors the gitea@cloonar.com / authelia@cloonar.com pattern. The alias
on noreply@fueltide.io is what smtpd_sender_login_maps uses to let the
supabase SASL user send as that address without tripping
reject_authenticated_sender_login_mismatch.
# on mail.cloonar.com
SMTP_PASS=$(openssl rand -base64 30 | tr -d '/+=' | head -c 32)
echo "SMTP_PASS (store this in SOPS, step 3): $SMTP_PASS"
CRYPT=$(mkpasswd -m sha-512 "$SMTP_PASS")
cat > /tmp/supabase.ldif <<EOF
dn: uid=supabase,ou=users,dc=cloonar,dc=com
objectClass: mailAccount
objectClass: inetOrgPerson
uid: supabase
cn: Supabase Auth
sn: Auth
mail: supabase@cloonar.com
mailSendOnly: TRUE
userPassword: {CRYPT}$CRYPT
description: SASL account for Supabase GoTrue outbound mail
dn: mail=noreply@fueltide.io,ou=aliases,dc=cloonar,dc=com
objectClass: mailAlias
mail: noreply@fueltide.io
maildrop: supabase@cloonar.com
EOF
ldapadd -x -D "cn=admin,dc=cloonar,dc=com" -W -f /tmp/supabase.ldif
rm /tmp/supabase.ldif
2. Generate the fueltide.io DKIM key
Selector is default to match the glob that rspamd's dkim_signing block
(hosts/mail/modules/rspamd.nix:15-19) watches for.
mkdir -p /tmp/dkim-gen && cd /tmp/dkim-gen
nix-shell -p rspamd --run \
"rspamadm dkim_keygen -s default -d fueltide.io -k fueltide.io.default.key"
# private key: fueltide.io.default.key -> goes into SOPS (step 3)
# public key: printed to stdout -> goes into DNS (step 4)
Wipe the temp dir once both are copied out.
3. SOPS edits (two files)
hosts/mail/secrets.yaml
nix-shell -p sops --run 'sops hosts/mail/secrets.yaml'
Add:
rspamd-dkim-fueltide-io-key: |
-----BEGIN PRIVATE KEY-----
<paste contents of /tmp/dkim-gen/fueltide.io.default.key>
-----END PRIVATE KEY-----
hosts/web-arm/secrets.yaml
nix-shell -p sops --run 'sops hosts/web-arm/secrets.yaml'
Inside the existing supabase-env multiline value, append four new lines
(these are sourced as shell variables by env-generate.sh):
SMTP_USER=supabase@cloonar.com
SMTP_PASS=<plaintext from step 1>
GOOGLE_CLIENT_ID=<from step 5>
GOOGLE_SECRET=<from step 5>
4. DNS records for fueltide.io
Add on whichever DNS provider hosts fueltide.io:
TXT @ v=spf1 mx a:mail.cloonar.com ~all
TXT default._domainkey v=DKIM1; k=rsa; p=<public key from step 2>
TXT _dmarc v=DMARC1; p=quarantine; rua=mailto:postmaster@cloonar.com; fo=1
PTR for mail.cloonar.com is already set (it's been sending for cloonar.com).
If fueltide.io has no MX record, outbound is fine but bounces from remote MTAs
won't route — acceptable for one-way transactional mail. Add an MX pointing at
mail.cloonar.com. if you want bounces to be received.
5. Google Cloud OAuth client (≈ 5 min)
- console.cloud.google.com → APIs & Services → OAuth consent screen.
External user type. App name
Fueltide, user support email, developer contact. Scopes:openid,email,profile. Submit (or keep in testing if only internal users). - Credentials → Create Credentials → OAuth client ID → Web application.
Name
Supabase. Authorised redirect URI:https://supabase.cloonar.com/auth/v1/callback. - Copy Client ID + Client Secret → into SOPS as
GOOGLE_CLIENT_IDandGOOGLE_SECRET.
6. Apple Developer — enable Sign in with Apple on the iOS App ID
Only one action, no keys or Services IDs:
- developer.apple.com → Certificates, IDs & Profiles → Identifiers → App
IDs. Select
io.fueltide.workout(TeamXWJ4DC7TBH, seehosts/web-arm/sites/fueltide.io.nix). Check Sign in with Apple. Save.
That's it on the Apple side. No Services ID, no Keys, no .p8 download.
The iOS app obtains the id_token on-device via AuthenticationServices
and posts it to supabase.auth.signInWithIdToken; GoTrue validates it
against Apple's JWKS with aud=io.fueltide.workout.
7. Merge and deploy
Once steps 1–6 are done:
./scripts/test-configuration web-arm
./scripts/test-configuration mail
git checkout master
git merge --no-ff <this-branch>
git push
Bento rolls out both hosts. On web-arm.cloonar.com:
sudo systemctl restart supabase-env-generate
sudo cat /run/supabase/auth.env # expect SMTP + Google vars populated
sudo podman exec supabase-auth nc -vz mail.cloonar.com 587
sudo podman restart supabase-auth
Verification checklist
/run/supabase/auth.envcontainsGOTRUE_SMTP_USER,GOTRUE_SMTP_PASS,GOTRUE_EXTERNAL_GOOGLE_CLIENT_ID,GOTRUE_EXTERNAL_GOOGLE_SECRET.podman inspect supabase-authshowsGOTRUE_EXTERNAL_APPLE_ENABLED=trueandGOTRUE_EXTERNAL_APPLE_CLIENT_ID=io.fueltide.workoutin the env.curl -X POST -H 'apikey: <anon>' -H 'Content-Type: application/json' \ https://supabase.cloonar.com/auth/v1/signup \ -d '{"email":"<real inbox>","password":"correct horse battery staple"}'delivers a mail withFrom: noreply@fueltide.iowithin ~30 s.- Mail headers show
dkim=pass,spf=pass,dmarc=pass(Authentication-Resultsheader). POST /auth/v1/recovertriggers a reset mail.- Browser visit to
https://supabase.cloonar.com/auth/v1/authorize?provider=googlecompletes and lands on/auth/v1/callback. Row inauth.identitieswithprovider='google'. - From the iOS app: Sign in with Apple →
supabase.auth.signInWithIdToken({ provider: 'apple', token, nonce })succeeds. Row inauth.identitieswithprovider='apple'andidentity_data.submatching the Apple user id. (Apple sign-in has no browser flow here — it is tested from the app only.) - Send a signup to mail-tester.com — target ≥ 9/10 spam score.
Rotation notes
- Google client secret: no expiry; rotate via Google Cloud console if
leaked and update
GOOGLE_SECRETin SOPS. - DKIM key: no expiry, but best practice is to rotate yearly. Rotation = regenerate keypair (step 2), replace the SOPS value (step 3), update DNS (step 4), deploy. Keep both old+new DNS records live for 24h during cutover.
- SMTP LDAP password: no expiry. To rotate, run
mkpasswdagain and update both the LDAP userPassword attribute and SOPSSMTP_PASS.