Commit Graph
1 Commits
  • Bind invites to a provider, and email them
    Wanted shape: the owner signs in with GitHub, everyone invited later signs in
    with Google. That was previously a convention, not a boundary -- may_login
    checked only that a live row existed, so any enabled provider could satisfy
    any invite.
    
    Now an invite is bound to the provider it was issued for. friend@gmail.com
    invited for Google cannot be redeemed over GitHub, and there is never a
    second, weaker door onto an existing account. The per-request re-check still
    passes provider=None, because there the question is only whether the account
    is still live.
    
    Two related fixes fell out:
    
      - upsert_user no longer refreshes `provider` from the login. It is part of
        the identity an admin issued and may_login now enforces it, so overwriting
        it on sign-in would let a successful login silently rewrite the very rule
        that permitted it.
      - a Google invite must be a full email address. A bare username would
        authorise something Google can never return, and the invitee would see
        only "not authorised" with no way to work out why.
    
    INVITE EMAILS. Adding a user now mails them, when SMTP is configured and there
    is an address -- for Google there always is, since the identity IS one. The
    response reports whether the mail actually went and why not, because "Invited"
    alone reads as success while the person waits for an email nobody sent.
    
    The link carries NO token, deliberately. The row already names the exact
    identity permitted and Google will not hand us an unverified address, so
    possession of the mail is not what authorises anyone: a forwarded invite
    grants nothing, nothing expires, and resend is always safe. A signed expiring
    token would only buy the ability to withdraw an unused invitation, which
    deleting the row already does.
    
    Resend is offered in the panel on anyone who has an address and has never
    signed in -- the likeliest reason being that they missed it.
    
    tests/test_invites.py: 29 assertions driving a real SMTP conversation against
    a sink server, plus the full provider-binding matrix. 119 tests pass across
    four suites.
    
    Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>