docs(19.5): decide goth for social sign-in

This commit is contained in:
Jakub Zych
2026-10-20 22:55:08 +02:00
parent c1ae1bb571
commit 26ebb6c98c
2 changed files with 55 additions and 0 deletions

View File

@@ -0,0 +1,54 @@
# Decision: social sign-in uses goth without gothic
**Date:** 2026-10-20
**Status:** Accepted
**Context:** Phase 19.5 (social login in a shared plugin, D-01, D-02, D-03, D-04), 19.5-RESEARCH.md Q1, Q2 and Package Legitimacy Audit
## Decision
Social sign-in (Google, Facebook, GitHub, Microsoft) lives in the new shared plugin `sm-sociallogin-plugin`, which requires `sm-user-plugin`. Its OAuth 2.0 client side uses goth's provider packages and drives them without goth's session layer.
- **`github.com/markbates/goth` pinned at v1.82.0.** Published 2025-08-18, MIT, origin `github.com/markbates/goth` at tag `v1.82.0`, `go 1.23.0` in its go.mod. It is the newest tag.
- **Only five packages are imported, and only by `sm-sociallogin-plugin`** (`classes/providers_goth.go`): the core package `github.com/markbates/goth` and `providers/google`, `providers/facebook`, `providers/github`, `providers/azureadv2`. No other repository imports goth.
- **`gothic` and `goth.UseProviders` are never used.** `gothic` is the only goth package that imports `github.com/gorilla/sessions`, which would be a second session mechanism next to `bouncer`. `goth.UseProviders` fills a process-global map, which does not fit per-request provider configuration. The plugin builds providers itself and calls `BeginAuth`, `Marshal`, `UnmarshalSession`, `Authorize` and `FetchUser` directly.
- **The goth session lives in the plugin's own sealed state cookie (D-07).** The marshalled goth session goes into an AES-GCM cookie keyed from the app key with a domain-separated HKDF label, together with the state, the provider and the action. No server-side session store.
- **Imported non-stdlib packages are exactly these** (measured with `go list -deps` on a scratch module importing the five packages):
- `cloud.google.com/go/compute/metadata`
- `github.com/markbates/goth`, `.../providers/azureadv2`, `.../providers/facebook`, `.../providers/github`, `.../providers/google`
- `golang.org/x/oauth2`, `golang.org/x/oauth2/authhandler`, `golang.org/x/oauth2/google`, `golang.org/x/oauth2/google/externalaccount`, `golang.org/x/oauth2/google/internal/externalaccountauthorizeduser`, `golang.org/x/oauth2/google/internal/impersonate`, `golang.org/x/oauth2/google/internal/stsexchange`, `golang.org/x/oauth2/internal`, `golang.org/x/oauth2/jws`, `golang.org/x/oauth2/jwt`
The core package needs only the standard library and `golang.org/x/oauth2`. `providers/google` adds `golang.org/x/oauth2/google` (for `google.Endpoint`), which brings `cloud.google.com/go/compute/metadata`. `facebook`, `github` and `azureadv2` add nothing beyond core.
- **`azureadv2` over `microsoftonline`.** Only `azureadv2` takes a tenant (`common`, `organizations`, `consumers` or a tenant ID) and it needs no extra module; `microsoftonline` hard-codes the `common` endpoints and pulls `github.com/markbates/going/defaults`. The wrapper names the provider `microsoft`, requests `offline_access` explicitly and ignores goth's auth-only Graph photo URL.
## Measurements
- **Binary size: +132 KiB** on a stripped build of the demo application (`-trimpath -ldflags="-s -w"`): 47,427,847 to 47,563,015 bytes, with the four providers driven through `BeginAuth`, `UnmarshalSession`, `Authorize` and `FetchUser`. The demo already links `google.golang.org/api` through gocloud, which keeps the delta small.
- **No gorilla session module in the binary.** `go version -m` lists `github.com/markbates/goth v1.82.0`, `golang.org/x/oauth2` and `cloud.google.com/go/compute/metadata`; the only gorilla module is the pre-existing `github.com/gorilla/css`. No `gorilla/sessions`, `securecookie`, `mux` or `pat`.
- **Module graph: 19 go.sum-only additions, no existing version changed.** A consuming module's `go list -m all` gains `github.com/decred/dcrd/dcrec/secp256k1/v4`, `github.com/go-chi/chi/v5`, `github.com/goccy/go-json`, `github.com/gorilla/context`, `github.com/gorilla/mux`, `github.com/gorilla/pat`, `github.com/gorilla/securecookie`, `github.com/gorilla/sessions`, `github.com/jarcoal/httpmock`, `github.com/lestrrat-go/backoff/v2`, `github.com/lestrrat-go/blackmagic`, `github.com/lestrrat-go/httpcc`, `github.com/lestrrat-go/iter`, `github.com/lestrrat-go/jwx`, `github.com/lestrrat-go/option`, `github.com/markbates/going`, `github.com/markbates/goth`, `github.com/mrjones/oauth` and `github.com/pkg/errors`. These come from goth's go.mod (gothic, other providers, tests); none of them is compiled into the binary.
- **Vulnerabilities:** `govulncheck` v1.9.0 over a scratch module with goth v1.82.0 reported "No vulnerabilities found."
## Maintenance risk
goth has 6.6k stars and 825 commits, but its repository advertises a search for a new maintainer and the last tag is 14 months old. D-03 contains that risk: goth stays behind the plugin's own `Provider` interface and never appears in its public API, each provider package is under 300 lines, and a provider can be replaced by a hand-written one (or a fork) without touching routes, payloads or the plugin's exported types.
## Rejected alternatives
- **Hand-written `golang.org/x/oauth2` plus profile calls.** Fewer dependencies, but the user prefers a maintained library for protocol code, and goth already normalises the four profile APIs.
- **`gorilla/securecookie` for the state cookie.** A new dependency for about 30 lines of standard-library AEAD (`crypto/hkdf`, `crypto/aes`, `crypto/cipher`).
- **`gothic`.** It brings `gorilla/sessions` as a second session mechanism and keeps the state in its own store; the state-check it does (`validateState`) also skips an empty original state, which the plugin must reject.
## Related dependencies
- **`golang.org/x/oauth2`**, already in the workspace module graph (approved in 19.4); minimal version selection picks the application's version (goth needs v0.27.0 or newer).
- **`cloud.google.com/go/compute/metadata`**, already present in the workspace graph.
## Package legitimacy
The package-legitimacy seam cannot check Go modules (as in 19.4), so goth is [ASSUMED] for legitimacy and is gated by a blocking human check (plan 19.5-01 Task 4) before any `go get`. Proxy signals gathered by hand: v1.82.0 on proxy.golang.org, origin URL and tag ref as above, MIT licence.
## Consequences
- The framework `go.mod` gains nothing; `grep markbates/goth go.mod` stays empty. goth is required only by `sm-sociallogin-plugin` and appears in the go.sum of applications that mount it.
- `go list -deps` of any application binary must not list `github.com/gorilla/sessions`.
- `gopkg.in/yaml.v3` appears in goth's module graph as an indirect module and is never imported (the project rule is `goccy/go-yaml`).
- PKCE stays off, because goth's providers keep their `oauth2.Config` unexported and expose no hook. RFC 9700 allows a browser-bound one-time `state` for confidential clients, which the sealed state cookie provides. PKCE is on the deferred list.

View File

@@ -46,6 +46,7 @@ This extends `.planning/research/go-ecosystem.md` (2026-09-16 GitHub-API-verifie
| stretchr/testify | v1.12.1 (Aug 17, 2026) | Assertions in Go tests | Standard choice; use `assert`/`require` for readability in the API-parity diff tests, not as a BDD framework — keep tests as plain `func Test...(*testing.T)`. |
| air-verse/air | v1.67.4 (Aug 1, 2026), actively maintained | Dev watch-rebuild loop | This is the "watch loop that rebuilds the compiled-plugin binary on change" the plugin architecture already calls for. Config via `.air.toml`; point `cmd`/`bin` at `go build -o ./tmp/summer ./cmd/summer && ./tmp/summer serve`. |
| golangci-lint | v2.13.2 (Aug 27, 2026) | Linting | v2's config schema (`version: "2"` in `.golangci.yml`) is a breaking change from v1 — do not copy a v1 config from an older Go project without migrating it. |
| `github.com/markbates/goth` (core plus providers google, facebook, github, azureadv2; never gothic) | v1.82.0 | OAuth 2.0 client for social sign-in in sm-sociallogin-plugin | Decided in .planning/notes/2026-10-20-sociallogin-goth.md (Phase 19.5 D-02); plugin-only, the framework go.mod does not require it. |
### Development Tools