Skip to content
This repository has been archived by the owner on Jul 12, 2023. It is now read-only.

Allow custom email server for invitations #796

Merged
merged 8 commits into from
Oct 9, 2020

Conversation

whaught
Copy link
Contributor

@whaught whaught commented Oct 8, 2020

Issue #787

Proposed Changes

  • This PR allows for a server-level override to sending new user invitations via a custom SMTP server
  • If no secret is present, it falls back to our firebase hack with the password-reset template

In the future we can use this to

  • Allow per-realm overrides of the email server
  • Allow per-realm templates for the invitation email itself

This gets around our server-side call to the firebase client API which may face IP rate limiting.
https://cloud.google.com/identity-platform/quotas#email_link_generation_limits_2
Note that generating the link has 10x quota and is intended to be called from the server.

We could someday send other kinds of emails (eg. TOTP for 2nd factor auth if we want to roll our own 2nd factor via this and Twilio), but that sort of thing is not currently planned.

Release Note

Allow for invitations from a custom SMTP server

@googlebot googlebot added the cla: yes Auto: added by CLA bot when all committers have signed a CLA. label Oct 8, 2020
Copy link
Contributor

@jeremyfaller jeremyfaller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing I've commented on here requires changing. Feel free to address in a followup if necessary.

@@ -49,7 +49,8 @@ func (c *Controller) ensureFirebaseUserExists(ctx context.Context, user *databas
}

if created {
if err := c.firebaseInternal.SendNewUserInvitation(ctx, user.Email); err != nil {
err := c.emailer.SendNewUserInvitation(ctx, user.Email)
if err != nil {
flash.Error("Could not send new user invitation: %v", err)
return true, err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dunno if we want to wrap errors. We're typically trying to.

pkg/email/smtp.go Outdated Show resolved Hide resolved

User string `env:"EMAIL_USER" json:",omitempty"`
Password string `env:"EMAIL_PASSWORD" json:",omitempty"`
SmtpHost string `env:"EMAIL_SMTP_HOST" json:",omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dunno why these are separate, especially when you just combine them later....

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lines like smtp.PlainAuth("", s.User, s.Password, s.SmtpHost) use the host alone, we add on the port only for the connection


User string
Password string
SmtpHost string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

@whaught whaught changed the title [WIP] Allow custom email server for invitations Allow custom email server for invitations Oct 9, 2020
// Sending email.
err = smtp.SendMail(s.SMTPHost+":"+s.SMTPPort, auth, s.User, []string{toEmail}, []byte(finalMessage))
if err != nil {
return err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrap, or don't bother setting err.


inviteLink, err := s.FirebaseAuth.PasswordResetLink(ctx, toEmail)
if err != nil {
return err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrap

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bump

@google-oss-robot
Copy link

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: jeremyfaller, whaught

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:
  • OWNERS [jeremyfaller,whaught]

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@google-oss-robot google-oss-robot merged commit 9dc1498 into google:main Oct 9, 2020
@whaught whaught deleted the send-email branch October 9, 2020 20:14
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 10, 2020

User string `env:"EMAIL_USER" json:",omitempty"`
Password string `env:"EMAIL_PASSWORD" json:",omitempty"`
SMTPHost string `env:"EMAIL_SMTP_HOST" json:",omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably worth noting that this won't work for most modern Gmail accounts, and won't work for G Suite Google Workplace either.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait... but it does. I'm running encvtest@gmail.com to test this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't have MFA enabled then? It won't work with advanced protection accounts either.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://support.google.com/mail/answer/185833

With MFA you need to create an app password for this to use

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

App Passwords aren’t recommended and are unnecessary in most cases.

If you don’t have this option, it might be because:

c. Your account is through work, school, or other organization.
d. You turned on Advanced Protection.

c is very likely. d will probably become a default in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

72cc99b

Adding in some notes

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank


type Provider interface {
// SendNewUserInvitation sends an invite to join the server.
SendNewUserInvitation(ctx context.Context, email string) error
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would probably be better to abstract the email content from the email sending. I'd expect the email provider to only know how to SendEmail:

type Provider interface {
  SendEmail(ctx context.Context, subject, to, cc, bcc string, body io.Reader)
}

func (s *SMTPProvider) SendNewUserInvitation(ctx context.Context, toEmail string) error {
// Header
header := make(map[string]string)
header["From"] = s.User
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should try to make most of these constants. Both the map keys and many of their values are constant.


inviteLink, err := s.FirebaseAuth.PasswordResetLink(ctx, toEmail)
if err != nil {
return err
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bump

header["Subject"] = "COVID-19 Verification Server Invitation"

header["MIME-Version"] = "1.0"
header["Content-Type"] = `text/html; charset="utf-8"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we want to send as text/html. The firebase ones are text/plain.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
cla: yes Auto: added by CLA bot when all committers have signed a CLA.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants