One-time OAuth login nonce for shared-playground logins (cubic P1)
Addresses the P1 from cubic's post-merge review of #35: bare playground ids are public, so the cookie==state CSRF check was vacuous on the member login path. Logins started from a bare id now carry an unguessable nonce echoed in an HttpOnly cookie; the callback requires the two to match and consumes the nonce. Legacy/bearer states unchanged. Tests in tests/sharing-canonical.test.ts cover the attack, the fixed flow, nonce single-use, and legacy compat.
Summary by cubic
Fixes a login CSRF vulnerability where shared-playground logins started from a bare (public) playground id could be replayed cross-browser, letting an attacker link their own GitHub account into a victim's member slot.
Logins from a bare id now carry an unguessable one-time nonce in an HttpOnly cookie; the callback requires the nonce to match the OAuth state and expires the cookie once the login is consumed, so a captured state can't be replayed. Legacy and bearer states are already unguessable and are unchanged.
Tests cover the attack (bare state without a nonce rejected even for valid members), the fixed flow, nonce single-use, and legacy compatibility.