Enables the auth providers and transactional email flows the self-hosted Supabase was missing compared to the cloud instance: - GoTrue now accepts Google and Apple OAuth (web flow); Apple client-secret JWT is signed fresh on every activation from the SOPS-stored .p8 so there's no 6-month rotation ritual. - SMTP points at mail.cloonar.com:587 with SASL auth via a new `supabase` LDAP account; a `noreply@fueltide.io` mailAlias lets that account send as the fueltide.io address. - rspamd on mail.cloonar.com gets a per-domain DKIM key for fueltide.io (selector `default`) so outbound mail is signed. - MAILER_AUTOCONFIRM is off so signup confirmation + password reset actually go through email. - SITE_URL + URI_ALLOW_LIST point at app.fueltide.io / stage so links in emails and OAuth redirects land in the right app. FUELTIDE_AUTH_SETUP.md documents the manual steps (LDAP entries, SOPS additions, DNS records, Google/Apple console setup) that must be completed before merging.
8.9 KiB
Supabase auth setup: Google + Apple OAuth, 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 + Apple OAuth, SMTP pointed atmail.cloonar.com:587,MAILER_AUTOCONFIRM=false,SITE_URL+URI_ALLOW_LISTfor fueltide.io, python+cryptography in the env-generate path (for Apple JWT signing).hosts/web-arm/modules/supabase/env-generate.sh— newauth.envblock that pulls SMTP + OAuth creds from SOPS and signs the Apple client-secret JWT fresh on every activation.hosts/mail/modules/dkim-fueltide.nix— installs a per-domain DKIM key for fueltide.io into rspamd so outbound mail fromnoreply@fueltide.iois signed.
Complete the seven steps below before merging to master. Merging without them will deploy a broken GoTrue (missing OAuth/SMTP creds → auth emails fail, 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 eight 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>
APPLE_TEAM_ID=XWJ4DC7TBH
APPLE_KEY_ID=<from step 6>
APPLE_SERVICES_ID=com.cloonar.supabase.fueltide
APPLE_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\n<.p8 body>\n-----END PRIVATE KEY-----
Note on APPLE_PRIVATE_KEY: it must be one line with literal backslash-n
separating the PEM lines (no real newlines inside the value). The python
signer in env-generate.sh un-escapes those via decode("unicode_escape")
before loading the PEM. To format an existing AuthKey_XXX.p8 as that single
line:
awk '{printf "%s\\n", $0}' AuthKey_XXXXXXXXXX.p8
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 Sign in with Apple (≈ 15 min, paid account required)
- developer.apple.com → Certificates, IDs & Profiles → Identifiers → +
→ Services IDs. Description
Fueltide Supabase Auth. Identifiercom.cloonar.supabase.fueltide. Check Sign in with Apple → Configure. - Primary App ID: existing
io.fueltide.workout(TeamXWJ4DC7TBH, seehosts/web-arm/sites/fueltide.io.nix). Domains and Subdomains:supabase.cloonar.com. Return URLs:https://supabase.cloonar.com/auth/v1/callback. Save. - Keys → + → name
Fueltide Supabase Auth→ check Sign in with Apple → Configure → primary App IDio.fueltide.workout. Register. - Download the
.p8file now — Apple only offers it once. - Note the Key ID (10 chars) displayed on the key page.
- Team ID is
XWJ4DC7TBH(already known). - Into SOPS on web-arm:
APPLE_TEAM_ID=XWJ4DC7TBHAPPLE_KEY_ID=<from step 5>APPLE_SERVICES_ID=com.cloonar.supabase.fueltideAPPLE_PRIVATE_KEY=<single-line .p8 as described in step 3>
iOS native flow (optional)
If the fueltide iOS app will use supabase.auth.signInWithIdToken({ provider: 'apple', token: identityToken }) (native AuthenticationServices SDK, no web
browser), the iOS bundle ID must also appear in GOTRUE_EXTERNAL_APPLE_CLIENT_ID.
Change the line in env-generate.sh that currently reads:
GOTRUE_EXTERNAL_APPLE_CLIENT_ID=${APPLE_SERVICES_ID:-}
to something like:
GOTRUE_EXTERNAL_APPLE_CLIENT_ID=${APPLE_SERVICES_ID:-},io.fueltide.workout
(GoTrue accepts a comma-separated audiences list here and validates incoming id_tokens against any of them.)
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 8 new vars populated
sudo podman exec supabase-auth nc -vz mail.cloonar.com 587
sudo podman restart supabase-auth
Verification checklist
/run/supabase/auth.envcontainsGOTRUE_EXTERNAL_APPLE_SECRET=<long-JWT>.- Second
systemctl restart supabase-env-generateproduces a different Apple JWT (freshness — signed with newiat). 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'. - Same with
?provider=applefrom a page Apple's Return URL accepts. - Send a signup to mail-tester.com — target ≥ 9/10 spam score.
Rotation notes
- Apple client-secret JWT: auto-regenerated on every activation
(
supabase-env-generate.service). No manual rotation. - Apple
.p8key: no expiry, but revoking it in the Apple console immediately breaks auth. If ever rotated, updateAPPLE_KEY_IDandAPPLE_PRIVATE_KEYin SOPS together. - 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.