Skip to content

Authentication Results

VaultSandbox validates email authentication for every received email, providing detailed SPF, DKIM, DMARC, and reverse DNS results.

Email authentication helps verify that an email:

  • Came from the claimed sender domain (SPF)
  • Wasn’t modified in transit (DKIM)
  • Complies with the domain’s policy (DMARC)
  • Came from a legitimate mail server (Reverse DNS)

All authentication result types support a "skipped" status, which indicates the check was not performed. This occurs when:

  • The inbox has emailAuth: false (created with WithEmailAuth(false))
  • The server has globally disabled that specific check
  • The master switch VSB_EMAIL_AUTH_ENABLED=false is set on the server

When authentication checks are skipped:

email, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(10*time.Second))
if err != nil {
log.Fatal(err)
}
if email.AuthResults.SPF != nil && email.AuthResults.SPF.Result == "skipped" {
fmt.Println("SPF check was skipped for this inbox")
}
// The Validate() method treats "skipped" as passing (not a failure)
validation := email.AuthResults.Validate()
fmt.Printf("Validation passed: %v\n", validation.Passed) // true if all are pass or skipped

Every email has an AuthResults field:

email, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(10*time.Second))
if err != nil {
log.Fatal(err)
}
auth := email.AuthResults
fmt.Printf("SPF: %+v\n", auth.SPF)
fmt.Printf("DKIM: %+v\n", auth.DKIM)
fmt.Printf("DMARC: %+v\n", auth.DMARC)
fmt.Printf("ReverseDNS: %+v\n", auth.ReverseDNS)

The AuthResults struct is defined in the authresults package:

import "github.com/vaultsandbox/client-go/authresults"
type AuthResults struct {
SPF *SPFResult
DKIM []DKIMResult
DMARC *DMARCResult
ReverseDNS *ReverseDNSResult
}

Verifies the sending server is authorized to send from the sender’s domain.

type SPFResult struct {
Result string // "pass", "fail", "softfail", "neutral", "none", "temperror", "permerror", "skipped"
Domain string
IP string
Details string
}
if email.AuthResults.SPF != nil {
spf := email.AuthResults.SPF
fmt.Printf("Result: %s\n", spf.Result)
fmt.Printf("Domain: %s\n", spf.Domain)
fmt.Printf("Details: %s\n", spf.Details)
}
StatusMeaning
passSending server is authorized
failSending server is NOT authorized
softfailProbably not authorized (policy says ~all)
neutralDomain makes no assertion
temperrorTemporary error during check
permerrorPermanent error in SPF record
noneNo SPF record found
skippedCheck was skipped (emailAuth disabled)
email, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(10*time.Second))
if err != nil {
t.Fatal(err)
}
if email.AuthResults.SPF != nil {
spf := email.AuthResults.SPF
if spf.Result != "pass" {
t.Errorf("expected SPF pass, got %s", spf.Result)
}
if spf.Domain != "example.com" {
t.Errorf("expected domain example.com, got %s", spf.Domain)
}
fmt.Printf("SPF %s for %s\n", spf.Result, spf.Domain)
}

Cryptographically verifies the email hasn’t been modified and came from the claimed domain.

type DKIMResult struct {
Result string // "pass", "fail", "none", "skipped"
Domain string
Selector string
Signature string
Info string
}
dkim := email.AuthResults.DKIM // []DKIMResult
if len(dkim) > 0 {
for _, result := range dkim {
fmt.Printf("Result: %s\n", result.Result)
fmt.Printf("Domain: %s\n", result.Domain)
fmt.Printf("Selector: %s\n", result.Selector)
fmt.Printf("Info: %s\n", result.Info)
}
}

Note: An email can have multiple DKIM signatures (one per signing domain).

StatusMeaning
passSignature is valid
failSignature is invalid
noneNo DKIM signature found
skippedCheck was skipped (emailAuth disabled)
email, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(10*time.Second))
if err != nil {
t.Fatal(err)
}
if len(email.AuthResults.DKIM) > 0 {
dkim := email.AuthResults.DKIM[0]
if dkim.Result != "pass" {
t.Errorf("expected DKIM pass, got %s", dkim.Result)
}
if dkim.Domain != "example.com" {
t.Errorf("expected domain example.com, got %s", dkim.Domain)
}
fmt.Printf("DKIM %s (%s._domainkey.%s)\n", dkim.Result, dkim.Selector, dkim.Domain)
}

DMARC (Domain-based Message Authentication)

Section titled “DMARC (Domain-based Message Authentication)”

Checks that SPF or DKIM align with the From address and enforces the domain’s policy.

type DMARCResult struct {
Result string // "pass", "fail", "none", "skipped"
Policy string // "none", "quarantine", "reject"
Aligned bool
Domain string
Info string
}
if email.AuthResults.DMARC != nil {
dmarc := email.AuthResults.DMARC
fmt.Printf("Result: %s\n", dmarc.Result)
fmt.Printf("Domain: %s\n", dmarc.Domain)
fmt.Printf("Policy: %s\n", dmarc.Policy)
fmt.Printf("Aligned: %v\n", dmarc.Aligned)
fmt.Printf("Info: %s\n", dmarc.Info)
}
StatusMeaning
passDMARC check passed (SPF or DKIM aligned)
failDMARC check failed
noneNo DMARC policy found
skippedCheck was skipped (emailAuth disabled)
PolicyMeaning
noneNo action (monitoring only)
quarantineTreat suspicious emails as spam
rejectReject emails that fail DMARC
email, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(10*time.Second))
if err != nil {
t.Fatal(err)
}
if email.AuthResults.DMARC != nil {
dmarc := email.AuthResults.DMARC
if dmarc.Result != "pass" {
t.Errorf("expected DMARC pass, got %s", dmarc.Result)
}
if dmarc.Domain != "example.com" {
t.Errorf("expected domain example.com, got %s", dmarc.Domain)
}
fmt.Printf("DMARC %s (policy: %s)\n", dmarc.Result, dmarc.Policy)
}

Verifies the sending server’s IP resolves to a hostname that matches the sending domain.

type ReverseDNSResult struct {
Result string // "pass", "fail", "none", "skipped"
IP string
Hostname string
}
if email.AuthResults.ReverseDNS != nil {
rdns := email.AuthResults.ReverseDNS
fmt.Printf("Result: %s\n", rdns.Result)
fmt.Printf("IP: %s\n", rdns.IP)
fmt.Printf("Hostname: %s\n", rdns.Hostname)
}
StatusMeaning
passReverse DNS verified
failReverse DNS check failed
noneNo reverse DNS record found
skippedCheck was skipped (emailAuth disabled)
email, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(10*time.Second))
if err != nil {
t.Fatal(err)
}
if email.AuthResults.ReverseDNS != nil {
rdns := email.AuthResults.ReverseDNS
fmt.Printf("Reverse DNS: %s -> %s\n", rdns.IP, rdns.Hostname)
fmt.Printf("Result: %s\n", rdns.Result)
// Check if verification passed
if rdns.Result == "pass" {
fmt.Println("Reverse DNS verified")
}
}

In previous versions, ReverseDNSResult used a Verified boolean field. This has been replaced with a Result string field for consistency with other auth results.

Before (v0.6.x):

if rdns.Verified {
// Reverse DNS passed
}

After (v0.7.0+):

if rdns.Result == "pass" {
// Reverse DNS passed
}

The Validate() method provides a summary of all authentication checks:

validation := email.AuthResults.Validate()
fmt.Printf("Passed: %v\n", validation.Passed)
fmt.Printf("SPF Passed: %v\n", validation.SPFPassed)
fmt.Printf("DKIM Passed: %v\n", validation.DKIMPassed)
fmt.Printf("DMARC Passed: %v\n", validation.DMARCPassed)
fmt.Printf("ReverseDNS Passed: %v\n", validation.ReverseDNSPassed)
fmt.Printf("Failures: %v\n", validation.Failures)
type AuthValidation struct {
Passed bool // True if SPF, DKIM, and DMARC all passed (or skipped)
SPFPassed bool // SPF passed or skipped
DKIMPassed bool // At least one DKIM passed, or all skipped
DMARCPassed bool // DMARC passed or skipped
ReverseDNSPassed bool // Reverse DNS passed or skipped
Failures []string // Slice of failure descriptions
}

Note: The Validate() method treats "skipped" status as passing (not a failure). This allows inboxes with disabled email authentication to still pass validation checks.

All checks pass:

validation := email.AuthResults.Validate()
// AuthValidation{
// Passed: true,
// SPFPassed: true,
// DKIMPassed: true,
// DMARCPassed: true,
// ReverseDNSPassed: true,
// Failures: [],
// }
if !validation.Passed {
t.Error("expected all authentication checks to pass")
}

Some checks fail:

validation := email.AuthResults.Validate()
// AuthValidation{
// Passed: false,
// SPFPassed: false,
// DKIMPassed: true,
// DMARCPassed: false,
// ReverseDNSPassed: true,
// Failures: []string{
// "SPF check failed: fail (domain: example.com)",
// "DMARC policy: fail (policy: reject)",
// },
// }
if !validation.Passed {
fmt.Println("Authentication failures:")
for _, failure := range validation.Failures {
fmt.Printf(" - %s\n", failure)
}
}

Quick check if all primary authentication checks passed:

if email.AuthResults.IsPassing() {
fmt.Println("All authentication checks passed")
} else {
fmt.Println("Some authentication checks failed")
}

Note: IsPassing() is equivalent to Validate().Passed and checks SPF, DKIM, and DMARC. Reverse DNS is not included in this check.

The authresults package also provides functions that return errors for validation:

import "github.com/vaultsandbox/client-go/authresults"
// Validate all checks
if err := authresults.Validate(email.AuthResults); err != nil {
log.Printf("validation failed: %v", err)
}
// Validate individual checks
if err := authresults.ValidateSPF(email.AuthResults); err != nil {
log.Printf("SPF failed: %v", err)
}
if err := authresults.ValidateDKIM(email.AuthResults); err != nil {
log.Printf("DKIM failed: %v", err)
}
if err := authresults.ValidateDMARC(email.AuthResults); err != nil {
log.Printf("DMARC failed: %v", err)
}
if err := authresults.ValidateReverseDNS(email.AuthResults); err != nil {
log.Printf("Reverse DNS failed: %v", err)
}
var (
ErrSPFFailed = errors.New("SPF check failed")
ErrDKIMFailed = errors.New("DKIM check failed")
ErrDMARCFailed = errors.New("DMARC check failed")
ErrReverseDNSFailed = errors.New("reverse DNS check failed")
ErrNoAuthResults = errors.New("no authentication results available")
)

Use errors.Is to check for specific failures with individual validation functions:

if err := authresults.ValidateSPF(email.AuthResults); err != nil {
if errors.Is(err, authresults.ErrSPFFailed) {
fmt.Println("SPF check failed - check your SPF record")
} else if errors.Is(err, authresults.ErrNoAuthResults) {
fmt.Println("No SPF results available")
}
}

The package-level Validate() function returns a *ValidationError when validation fails, which contains all individual error messages:

type ValidationError struct {
Errors []string
}
func (e *ValidationError) Error() string // Returns errors joined with "; "

This allows you to access individual failure messages:

if err := authresults.Validate(email.AuthResults); err != nil {
// Type assert to access individual errors
if ve, ok := err.(*authresults.ValidationError); ok {
for _, msg := range ve.Errors {
fmt.Printf("- %s\n", msg)
}
}
}

Note: The package-level Validate() function returns *ValidationError or ErrNoAuthResults, while the individual functions (ValidateSPF, ValidateDKIM, etc.) return their respective sentinel errors.

func TestEmailPassesAllAuthenticationChecks(t *testing.T) {
sendEmail(inbox.EmailAddress())
email, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(10*time.Second))
if err != nil {
t.Fatal(err)
}
validation := email.AuthResults.Validate()
if !validation.Passed {
t.Errorf("expected all checks to pass, failures: %v", validation.Failures)
}
if !validation.SPFPassed {
t.Error("expected SPF to pass")
}
if !validation.DKIMPassed {
t.Error("expected DKIM to pass")
}
if !validation.DMARCPassed {
t.Error("expected DMARC to pass")
}
}
func TestEmailHasValidDKIMSignature(t *testing.T) {
sendEmail(inbox.EmailAddress())
email, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(10*time.Second))
if err != nil {
t.Fatal(err)
}
// Only check DKIM (most reliable)
if len(email.AuthResults.DKIM) == 0 {
t.Fatal("expected DKIM results")
}
if email.AuthResults.DKIM[0].Result != "pass" {
t.Errorf("expected DKIM pass, got %s", email.AuthResults.DKIM[0].Result)
}
}
func TestHandlesEmailsWithoutAuthentication(t *testing.T) {
email, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(10*time.Second))
if err != nil {
t.Fatal(err)
}
// Some senders don't have SPF/DKIM configured
validation := email.AuthResults.Validate()
// Log results for debugging
if !validation.Passed {
t.Logf("Auth failures (expected for test emails): %v", validation.Failures)
}
}
func TestEmailAuthentication(t *testing.T) {
ctx := context.Background()
inbox, err := client.CreateInbox(ctx)
if err != nil {
t.Fatal(err)
}
defer inbox.Delete(ctx)
sendEmail(inbox.EmailAddress())
email, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(10*time.Second))
if err != nil {
t.Fatal(err)
}
t.Run("SPF check", func(t *testing.T) {
if email.AuthResults.SPF != nil {
result := email.AuthResults.SPF.Result
if result != "pass" && result != "neutral" && result != "softfail" {
t.Errorf("unexpected SPF result: %s", result)
}
}
})
t.Run("DKIM check", func(t *testing.T) {
if len(email.AuthResults.DKIM) > 0 {
anyPassed := false
for _, d := range email.AuthResults.DKIM {
if d.Result == "pass" {
anyPassed = true
break
}
}
if !anyPassed {
t.Error("expected at least one DKIM signature to pass")
}
}
})
t.Run("DMARC check", func(t *testing.T) {
if email.AuthResults.DMARC != nil {
result := email.AuthResults.DMARC.Result
if result != "pass" && result != "none" {
t.Errorf("unexpected DMARC result: %s", result)
}
}
})
}

Testing authentication catches issues like:

  • Misconfigured SPF records - emails rejected by Gmail/Outlook
  • Missing DKIM signatures - reduced deliverability
  • DMARC failures - emails sent to spam
  • Reverse DNS mismatches - flagged as suspicious
func TestProductionEmailConfiguration(t *testing.T) {
app.SendWelcomeEmail(inbox.EmailAddress())
email, err := inbox.WaitForEmail(ctx, vaultsandbox.WithWaitTimeout(10*time.Second))
if err != nil {
t.Fatal(err)
}
validation := email.AuthResults.Validate()
// In production, these should all pass
if !validation.Passed {
t.Log("Email authentication issues detected:")
for _, f := range validation.Failures {
t.Logf(" %s", f)
}
t.Log("")
t.Log("Action required:")
if !validation.SPFPassed {
t.Log("- Fix SPF record for your domain")
}
if !validation.DKIMPassed {
t.Log("- Configure DKIM signing in your email service")
}
if !validation.DMARCPassed {
t.Log("- Add/fix DMARC policy")
}
t.Fail()
}
}
if email.AuthResults.SPF == nil &&
len(email.AuthResults.DKIM) == 0 &&
email.AuthResults.DMARC == nil {
fmt.Println("No authentication performed")
fmt.Println("This may happen for:")
fmt.Println("- Emails sent from localhost/internal servers")
fmt.Println("- Test SMTP servers without authentication")
}
validation := email.AuthResults.Validate()
if !validation.Passed {
fmt.Printf("Authentication failed: %v\n", validation.Failures)
// Common causes:
// 1. No SPF record: Add "v=spf1 ip4:YOUR_IP -all" to DNS
// 2. No DKIM: Configure your mail server to sign emails
// 3. No DMARC: Add "v=DMARC1; p=none" to DNS
// 4. Wrong IP: Update SPF record with correct server IP
}
validation := email.AuthResults.Validate()
for _, failure := range validation.Failures {
switch {
case strings.Contains(failure, "SPF"):
fmt.Println("Fix SPF: Update DNS TXT record for your domain")
case strings.Contains(failure, "DKIM"):
fmt.Println("Fix DKIM: Enable DKIM signing in your email service")
case strings.Contains(failure, "DMARC"):
fmt.Println("Fix DMARC: Add DMARC policy to DNS")
}
}