Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / internal/modules/auth/invite_delivery.go |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | internal/modules/auth/invite_delivery.go |
| Original SHA-256 | bedc68798cd2d8a52efe1cec89ac3d5ecc1186442009be0e55124099f8dd9f2f |
| Git blob SHA | c554794f78aef47801e940896b29b3ad7a887963 |
| Reading-copy SHA-256 | bedc68798cd2d8a52efe1cec89ac3d5ecc1186442009be0e55124099f8dd9f2f |
Recovered source transcript
Download complete source text ↓153 lines · 6.7 KB
package auth
// INVITE DELIVERY THROUGH THE ORGANIZATION'S OWN MAIL SERVER.
//
// Until now this platform created an invite and handed the one time link back to whoever pressed the button,
// who then delivered it by hand. That was the recorded Phase 2 decision and it was the right one at the time:
// there was no mail transport, and inventing one that sent from a missionMATCH address would have meant every
// customer's staff receiving activation links from a domain they had never heard of, which is exactly what
// people are trained to treat as phishing.
//
// So delivery uses THE CUSTOMER'S OWN mail server and THEIR OWN sending address. An invitation from
// talent@theiragency.com arrives looking like what it is, passes their own SPF and DKIM because it genuinely
// came from them, and lands in an inbox rather than a spam folder.
//
// TWO PROPERTIES ARE LOAD BEARING.
//
// IT DEGRADES TO THE OLD BEHAVIOUR, ALWAYS. An organization that has not configured mail, or whose mail
// server refuses the message, gets the link in the response exactly as before. A failed send never fails the
// invitation: the account is created and the token is valid either way, because losing a colleague's account
// creation over a mail server outage would be a worse product than the manual one this replaces.
//
// WHEN DELIVERY SUCCEEDS THE TOKEN IS WITHHELD. This is the security half. The token is a live credential
// that redeems into an account; once it has reached the intended person by mail, there is no reason for a
// copy of it to sit in an administrator's browser, in their network log, or in whatever they pasted it into.
// Successful delivery therefore returns delivered true and NO token.
import (
"context"
"errors"
"fmt"
"net/url"
"strings"
"time"
"github.com/jackc/pgx/v5"
"github.com/missionworkforce/missionmatch/internal/platform/mailer"
"github.com/missionworkforce/missionmatch/internal/platform/orgsettings"
)
// deliverInvite attempts to mail an activation link. It reports whether the message was actually accepted by
// the customer's mail server, and NEVER returns an error that should fail the invitation.
//
// It takes the token by value and holds it only for the length of the call. The token is written into the
// message body and nowhere else: not the audit trail, not the settings row, not a log line.
func (s *Service) deliverInvite(ctx context.Context, toEmail, toName, token string) (delivered bool, reason string) {
if s.deps.Settings == nil {
return false, "no settings store on this node"
}
if toEmail == "" || token == "" {
return false, "nothing to deliver"
}
var cfg mailer.Config
var orgName string
err := s.deps.DB.RunInTenantTx(ctx, func(ctx context.Context, tx pgx.Tx) error {
st, e := s.deps.Settings.LoadTx(ctx, tx)
if e != nil {
return e
}
// THREE CONDITIONS, ALL REQUIRED. Switched on, proven to work, and actually configured. Verification
// is checked rather than trusted because a settings row can be edited by hand and because the whole
// point of smtp_verified_at is that somebody proved this path once.
if !st.EmailDeliveryEnabled || st.SMTPVerifiedAt == nil {
return errDeliveryOff
}
if st.SMTPHost == "" || st.SMTPPort == 0 || st.EmailFromAddress == "" {
return errDeliveryOff
}
pw, e := s.deps.Settings.OpenSecret(ctx, tx, orgsettings.SecretSMTPPassword)
if e != nil && !errors.Is(e, orgsettings.ErrNoSecret) {
return e
}
cfg = mailer.Config{
Host: st.SMTPHost, Port: st.SMTPPort, Username: st.SMTPUsername, Password: pw,
Security: st.SMTPSecurity, From: st.EmailFromAddress, FromName: st.EmailFromName,
ReplyTo: st.EmailReplyTo,
}
// Best effort: a missing organization name costs a slightly plainer subject line, nothing more.
_ = tx.QueryRow(ctx, `SELECT name FROM organizations LIMIT 1`).Scan(&orgName)
return nil
})
if err != nil {
if errors.Is(err, errDeliveryOff) {
return false, "email delivery is not enabled and verified for this organization"
}
return false, "could not read the mail configuration"
}
sendCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
if err := mailer.Send(sendCtx, cfg, mailer.Message{
To: toEmail,
Subject: inviteSubject(orgName),
Body: inviteBody(orgName, toName, s.deps.AppBaseURL, token, s.deps.InviteSeconds),
}); err != nil {
// Recorded on the settings row so an administrator sees that delivery is failing without having to
// read a server log. Scrubbed of the credentials by the store before it is written.
_ = s.deps.DB.RunInTenantTx(ctx, func(ctx context.Context, tx pgx.Tx) error {
return s.deps.Settings.MarkAttemptFailed(ctx, tx, err.Error(), cfg.Username, cfg.Password)
})
return false, "the mail server refused the message, the link is returned instead"
}
return true, ""
}
var errDeliveryOff = errors.New("auth: organization mail delivery is off")
func inviteSubject(orgName string) string {
if orgName != "" {
return "You have been invited to " + orgName + " on missionMATCH"
}
return "You have been invited to missionMATCH"
}
// inviteBody is deliberately plain and short. It says who invited them, what to do, and when it stops
// working. It contains no images, no tracking, and no marketing, because this is an account credential
// arriving at a cleared workforce professional and it should read like one.
func inviteBody(orgName, toName, baseURL, token string, expirySeconds int) string {
greeting := "Hello,"
if n := strings.TrimSpace(toName); n != "" {
greeting = "Hello " + n + ","
}
who := "your organization"
if orgName != "" {
who = orgName
}
hours := expirySeconds / 3600
if hours < 1 {
hours = 1
}
var b strings.Builder
b.WriteString(greeting + "\r\n\r\n")
b.WriteString(who + " has invited you to missionMATCH.\r\n\r\n")
b.WriteString("Open this link to choose a password and activate your account:\r\n\r\n")
b.WriteString(inviteLink(baseURL, token) + "\r\n\r\n")
b.WriteString(fmt.Sprintf("The link works once and expires in %d hours.\r\n\r\n", hours))
b.WriteString("If you were not expecting this invitation, you can ignore this message and the link " +
"will expire on its own. Do not forward it to anyone: it grants access to your account.\r\n")
return b.String()
}
// inviteLink builds the activation URL. When no base URL is configured it returns the RAW TOKEN with a plain
// instruction rather than a broken link to a guessed host, because a link to the wrong hostname is worse than
// no link: it either fails silently or sends a live credential somewhere it does not belong.
func inviteLink(baseURL, token string) string {
base := strings.TrimRight(strings.TrimSpace(baseURL), "/")
if base == "" {
return "Your activation code is " + token + "\r\n(Ask your administrator for the sign in address.)"
}
return base + "/activate?token=" + url.QueryEscape(token)
}