#83 shipped sign-in as magic link only, which leaves exactly one way into the product — and it runs through an email provider we do not control. GitHub OAuth is a second door.
Why, and the order of the reasons matters
Redundancy first. Under magic link, one email is one sign-in, so an email outage or a deliverability problem is a total authentication outage — practitioners and admins alike, with no way in for anyone. A second provider turns that into degraded sign-in rather than no sign-in. #113 makes the email path as solid as it can reasonably be; this makes it non-singular.
Fit second. This directory lists Claude practitioners, so essentially all of them have a GitHub account, and one click beats waiting for mail.
The catch, and it is the interesting part
GitHub OAuth does not give us a claiming path. #14 matches a claimer against their verified account email to let them take over a curated profile, and GitHub returns a no-reply address by default, which can never match practitioner_contacts.contact_email. So a practitioner who signs in with GitHub and finds their curated profile is stuck — the exact case curated intake exists to serve.
That is not automatically fatal. Requesting the user:email scope gets the account's verified addresses from the GitHub API, which would restore the match. Whether to ask for that scope is the decision this issue exists to take, and it is a real trade: it widens what we ask permission for at the moment somebody is deciding whether to trust us, on a product whose whole proposition is trust. The alternatives are that GitHub users claim via magic link instead (they can — the two providers land on the same account if the email matches), or that an admin overrides by hand.
Decide it here rather than discovering it when the first curated practitioner cannot claim.
Scope
- Register a GitHub OAuth app; client ID and secret go in the Supabase dashboard, never in this repository.
- Enable the provider locally in
config.toml and on the hosted project.
- A button on
/sign-in. The existing /auth/callback route already exchanges the code, so this is not a second flow.
- Whatever the scope decision above requires.
Care
- Nothing secret enters this repository. The provider secret lives in Supabase, not Vercel and not
.env.local.
supabase config push remains a trap — it pushes config.toml wholesale, including the localhost site_url and redirect URLs, which would aim every production magic link at a developer's machine.
- There are no preview deployments, so the hosted callback URL is proved for the first time in production.
- The
bluehex_admin claim comes from custom_access_token_hook and is provider-agnostic, so nothing about admin authority changes.
Done when
- GitHub sign-in works locally and on the hosted project.
- The claiming question above is answered and written down — either the scope is requested and matching works, or the fallback is documented where a confused practitioner's first responder will find it.
- Signing in with GitHub and with a magic link on the same verified address lands on one account, not two.
Related: #83 (the magic-link sign-in), #113 (the email provider this de-risks), #14 (the claim path this complicates).
#83 shipped sign-in as magic link only, which leaves exactly one way into the product — and it runs through an email provider we do not control. GitHub OAuth is a second door.
Why, and the order of the reasons matters
Redundancy first. Under magic link, one email is one sign-in, so an email outage or a deliverability problem is a total authentication outage — practitioners and admins alike, with no way in for anyone. A second provider turns that into degraded sign-in rather than no sign-in. #113 makes the email path as solid as it can reasonably be; this makes it non-singular.
Fit second. This directory lists Claude practitioners, so essentially all of them have a GitHub account, and one click beats waiting for mail.
The catch, and it is the interesting part
GitHub OAuth does not give us a claiming path. #14 matches a claimer against their verified account email to let them take over a curated profile, and GitHub returns a
no-replyaddress by default, which can never matchpractitioner_contacts.contact_email. So a practitioner who signs in with GitHub and finds their curated profile is stuck — the exact case curated intake exists to serve.That is not automatically fatal. Requesting the
user:emailscope gets the account's verified addresses from the GitHub API, which would restore the match. Whether to ask for that scope is the decision this issue exists to take, and it is a real trade: it widens what we ask permission for at the moment somebody is deciding whether to trust us, on a product whose whole proposition is trust. The alternatives are that GitHub users claim via magic link instead (they can — the two providers land on the same account if the email matches), or that an admin overrides by hand.Decide it here rather than discovering it when the first curated practitioner cannot claim.
Scope
config.tomland on the hosted project./sign-in. The existing/auth/callbackroute already exchanges the code, so this is not a second flow.Care
.env.local.supabase config pushremains a trap — it pushesconfig.tomlwholesale, including the localhostsite_urland redirect URLs, which would aim every production magic link at a developer's machine.bluehex_adminclaim comes fromcustom_access_token_hookand is provider-agnostic, so nothing about admin authority changes.Done when
Related: #83 (the magic-link sign-in), #113 (the email provider this de-risks), #14 (the claim path this complicates).