Authentication Results
VaultSandbox validates email authentication for every received email, providing detailed SPF, DKIM, DMARC, and reverse DNS results.
What is Email Authentication?
Section titled “What is Email Authentication?”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)
AuthResults Object
Section titled “AuthResults Object”Every email has an authResults property:
Email email = inbox.waitForEmail(Duration.ofSeconds(10));
AuthResults auth = email.getAuthResults();
System.out.println(auth.getSpf()); // SPF resultSystem.out.println(auth.getDkim()); // DKIM results (list)System.out.println(auth.getDmarc()); // DMARC resultSystem.out.println(auth.getReverseDns()); // Reverse DNS resultSPF (Sender Policy Framework)
Section titled “SPF (Sender Policy Framework)”Verifies the sending server is authorized to send from the sender’s domain.
SPF Result Structure
Section titled “SPF Result Structure”SpfResult spf = email.getAuthResults().getSpf();
if (spf != null) { System.out.println(spf.getResult()); // "pass", "fail", "softfail", etc. System.out.println(spf.getDomain()); // Domain checked System.out.println(spf.getIp()); // IP address being validated System.out.println(spf.getDetails()); // Additional explanation}SPF Status Values
Section titled “SPF Status Values”| Status | Meaning |
|---|---|
pass | Sending server is authorized |
fail | Sending server is NOT authorized |
softfail | Probably not authorized (policy says ~all) |
neutral | Domain makes no assertion |
temperror | Temporary error during check |
permerror | Permanent error in SPF record |
none | No SPF record found |
skipped | Check was skipped (inbox has emailAuth: false) |
SpfResult Properties
Section titled “SpfResult Properties”| Property | Type | Description |
|---|---|---|
result | String | SPF check result: pass, fail, softfail, neutral, none, temperror, permerror, skipped |
domain | String | Domain being checked |
ip | String | IP address of the sending server |
details | String | Additional explanation about the result |
SpfResult Methods
Section titled “SpfResult Methods”| Method | Return Type | Description |
|---|---|---|
getResult() | String | Returns the SPF result |
getDomain() | String | Returns the checked domain |
getIp() | String | Returns the sending server IP |
getDetails() | String | Returns additional explanation |
SPF Example
Section titled “SPF Example”Email email = inbox.waitForEmail(Duration.ofSeconds(10));
SpfResult spf = email.getAuthResults().getSpf();if (spf != null) { assertThat(spf.getResult()).isEqualTo("pass"); assertThat(spf.getDomain()).isEqualTo("example.com");
System.out.printf("SPF %s for %s%n", spf.getResult(), spf.getDomain());}DKIM (DomainKeys Identified Mail)
Section titled “DKIM (DomainKeys Identified Mail)”Cryptographically verifies the email hasn’t been modified and came from the claimed domain.
DKIM Result Structure
Section titled “DKIM Result Structure”List<DkimResult> dkim = email.getAuthResults().getDkim(); // List of results
if (dkim != null && !dkim.isEmpty()) { for (DkimResult result : dkim) { System.out.println(result.getResult()); // "pass", "fail", "none" System.out.println(result.getDomain()); // Signing domain System.out.println(result.getSelector()); // DKIM selector System.out.println(result.getSignature()); // DKIM signature info }}Note: An email can have multiple DKIM signatures (one per signing domain).
DKIM Status Values
Section titled “DKIM Status Values”| Status | Meaning |
|---|---|
pass | Signature is valid |
fail | Signature is invalid |
none | No DKIM signature found |
skipped | Check was skipped (inbox has emailAuth: false) |
DkimResult Properties
Section titled “DkimResult Properties”| Property | Type | Description |
|---|---|---|
result | String | DKIM verification result: pass, fail, none, skipped |
domain | String | Signing domain |
selector | String | DKIM selector (identifies the public key in DNS) |
signature | String | DKIM signature information |
DkimResult Methods
Section titled “DkimResult Methods”| Method | Return Type | Description |
|---|---|---|
getResult() | String | Returns the DKIM result |
getDomain() | String | Returns the signing domain |
getSelector() | String | Returns the DKIM selector |
getSignature() | String | Returns DKIM signature info |
DKIM Example
Section titled “DKIM Example”Email email = inbox.waitForEmail(Duration.ofSeconds(10));
List<DkimResult> dkim = email.getAuthResults().getDkim();if (dkim != null && !dkim.isEmpty()) { DkimResult first = dkim.get(0);
assertThat(first.getResult()).isEqualTo("pass"); assertThat(first.getDomain()).isEqualTo("example.com");
System.out.printf("DKIM %s (%s._domainkey.%s)%n", first.getResult(), first.getSelector(), first.getDomain());}Check If Any DKIM Signature Passes
Section titled “Check If Any DKIM Signature Passes”List<DkimResult> dkim = email.getAuthResults().getDkim();boolean anyPass = dkim != null && dkim.stream() .anyMatch(d -> "pass".equalsIgnoreCase(d.getResult()));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.
DMARC Result Structure
Section titled “DMARC Result Structure”DmarcResult dmarc = email.getAuthResults().getDmarc();
if (dmarc != null) { System.out.println(dmarc.getResult()); // "pass", "fail", "none" System.out.println(dmarc.getDomain()); // Domain checked System.out.println(dmarc.getPolicy()); // Domain's policy System.out.println(dmarc.getAligned()); // Whether SPF/DKIM align with From header System.out.println(dmarc.isAligned()); // Convenience: true if aligned}DMARC Status Values
Section titled “DMARC Status Values”| Status | Meaning |
|---|---|
pass | DMARC check passed (SPF or DKIM aligned) |
fail | DMARC check failed |
none | No DMARC policy found |
skipped | Check was skipped (inbox has emailAuth: false) |
DMARC Policies
Section titled “DMARC Policies”| Policy | Meaning |
|---|---|
none | No action (monitoring only) |
quarantine | Treat suspicious emails as spam |
reject | Reject emails that fail DMARC |
DmarcResult Properties
Section titled “DmarcResult Properties”| Property | Type | Description |
|---|---|---|
result | String | DMARC check result: pass, fail, none, skipped |
domain | String | From domain being checked |
policy | String | Domain’s DMARC policy: none, quarantine, or reject |
aligned | Boolean | Whether SPF/DKIM results align with the From header domain |
DmarcResult Methods
Section titled “DmarcResult Methods”| Method | Return Type | Description |
|---|---|---|
getResult() | String | Returns the DMARC result |
getDomain() | String | Returns the From domain |
getPolicy() | String | Returns the domain’s DMARC policy |
getAligned() | Boolean | Returns alignment status (may be null) |
isAligned() | boolean | Convenience: true if aligned, false otherwise |
DMARC Example
Section titled “DMARC Example”Email email = inbox.waitForEmail(Duration.ofSeconds(10));
DmarcResult dmarc = email.getAuthResults().getDmarc();if (dmarc != null) { assertThat(dmarc.getResult()).isEqualTo("pass"); assertThat(dmarc.getDomain()).isEqualTo("example.com");
System.out.printf("DMARC %s (policy: %s)%n", dmarc.getResult(), dmarc.getPolicy());}Reverse DNS
Section titled “Reverse DNS”Verifies the sending server’s IP resolves to a hostname that matches the sending domain.
Reverse DNS Result Structure
Section titled “Reverse DNS Result Structure”ReverseDnsResult reverseDns = email.getAuthResults().getReverseDns();
if (reverseDns != null) { System.out.println(reverseDns.getResult()); // "pass", "fail", "none", "skipped" System.out.println(reverseDns.getIp()); // IP address being validated System.out.println(reverseDns.getHostname()); // Resolved hostname}Reverse DNS Status Values
Section titled “Reverse DNS Status Values”| Status | Meaning |
|---|---|
pass | PTR record matches and resolves correctly |
fail | PTR record doesn’t match or doesn’t resolve |
none | No PTR record found |
skipped | Check was skipped (inbox has emailAuth: false) |
ReverseDnsResult Properties
Section titled “ReverseDnsResult Properties”| Property | Type | Description |
|---|---|---|
result | String | Reverse DNS result: pass, fail, none, skipped |
ip | String | IP address of the sending server |
hostname | String | Resolved hostname from PTR record |
ReverseDnsResult Methods
Section titled “ReverseDnsResult Methods”| Method | Return Type | Description |
|---|---|---|
getResult() | String | Returns the reverse DNS result |
isVerified() | boolean | Convenience: returns true if result is “pass” |
getIp() | String | Returns the IP address being validated |
getHostname() | String | Returns the resolved hostname |
Reverse DNS Example
Section titled “Reverse DNS Example”Email email = inbox.waitForEmail(Duration.ofSeconds(10));
ReverseDnsResult rdns = email.getAuthResults().getReverseDns();if (rdns != null) { System.out.printf("Reverse DNS: %s for %s -> %s%n", rdns.getResult(), rdns.getIp(), rdns.getHostname());
if ("pass".equals(rdns.getResult())) { System.out.println("Reverse DNS verification passed"); }}Understanding the “skipped” Status
Section titled “Understanding the “skipped” Status”All authentication checks can return skipped as a result. This happens when:
- Inbox has
emailAuth: false- Authentication was disabled when creating the inbox - Server has globally disabled that check - The server configuration skips certain checks
- Master switch is disabled - Server has
VSB_EMAIL_AUTH_ENABLED=false
Handling Skipped Results
Section titled “Handling Skipped Results”Email email = inbox.waitForEmail(Duration.ofSeconds(10));
SpfResult spf = email.getAuthResults().getSpf();if ("skipped".equals(spf.getResult())) { // SPF check was disabled for this inbox System.out.println("SPF check was skipped");}
// The validate() method treats "skipped" as passing (not a failure)AuthValidation validation = email.getAuthResults().validate();// skipped results don't appear in getFailed()When to Use emailAuth: false
Section titled “When to Use emailAuth: false”Disabling email authentication is useful when:
- Testing with SMTP servers that don’t have proper DNS records
- Local development environments
- Testing email content without caring about authentication
- High-volume testing where auth checks add latency
// Create inbox without authentication checksInbox inbox = client.createInbox( CreateInboxOptions.builder() .emailAuth(false) .build());Validation Helper
Section titled “Validation Helper”The validate() method provides a summary of all authentication checks. Note that skipped results are treated as passing (they don’t appear in the failed list):
AuthValidation validation = email.getAuthResults().validate();
System.out.println(validation.isFullyAuthenticated()); // All checks passedSystem.out.println(validation.isPassed()); // Alias for isFullyAuthenticated()System.out.println(validation.hasSpf()); // SPF check passedSystem.out.println(validation.hasDkim()); // DKIM check passedSystem.out.println(validation.hasDmarc()); // DMARC check passedSystem.out.println(validation.hasReverseDns()); // Reverse DNS check passedSystem.out.println(validation.getPassed()); // List of passed checksSystem.out.println(validation.getFailed()); // List of failure reasonsSystem.out.println(validation.getFailures()); // Alias for getFailed()AuthValidation Methods
Section titled “AuthValidation Methods”| Method | Return Type | Description |
|---|---|---|
isFullyAuthenticated() | boolean | True if all checks passed and at least one exists |
isPassed() | boolean | Alias for isFullyAuthenticated() |
hasSpf() | boolean | SPF check passed |
hasDkim() | boolean | At least one DKIM signature passed |
hasDmarc() | boolean | DMARC check passed |
hasReverseDns() | boolean | Reverse DNS check passed |
getPassed() | List<String> | List of passed check names |
getFailed() | List<String> | List of failure descriptions |
getFailures() | List<String> | Alias for getFailed() |
Validation Examples
Section titled “Validation Examples”All checks pass:
AuthValidation validation = email.getAuthResults().validate();
// validation.getPassed() = ["SPF", "DKIM", "DMARC", "ReverseDNS"]// validation.getFailed() = []// validation.isFullyAuthenticated() = true
assertThat(validation.isFullyAuthenticated()).isTrue();assertThat(validation.getFailed()).isEmpty();Some checks fail:
AuthValidation validation = email.getAuthResults().validate();
// validation.getPassed() = ["DKIM", "ReverseDNS"]// validation.getFailed() = ["SPF: fail", "DMARC: fail"]// validation.isFullyAuthenticated() = false
if (!validation.isFullyAuthenticated()) { System.err.println("Authentication failures:"); for (String failure : validation.getFailed()) { System.err.println(" - " + failure); }}Testing Patterns
Section titled “Testing Patterns”Strict Authentication
Section titled “Strict Authentication”@Testvoid shouldPassAllAuthenticationChecks() { sendEmail(inbox.getEmailAddress());
Email email = inbox.waitForEmail(Duration.ofSeconds(10)); AuthValidation validation = email.getAuthResults().validate();
assertThat(validation.isFullyAuthenticated()).isTrue(); assertThat(validation.hasSpf()).isTrue(); assertThat(validation.hasDkim()).isTrue(); assertThat(validation.hasDmarc()).isTrue();}Lenient Authentication
Section titled “Lenient Authentication”@Testvoid shouldHaveValidDkimSignature() { sendEmail(inbox.getEmailAddress());
Email email = inbox.waitForEmail(Duration.ofSeconds(10)); List<DkimResult> dkim = email.getAuthResults().getDkim();
// Only check DKIM (most reliable) assertThat(dkim).isNotNull(); assertThat(dkim).isNotEmpty(); assertThat(dkim.get(0).getResult()).isEqualTo("pass");}Handling Missing Authentication
Section titled “Handling Missing Authentication”@Testvoid shouldHandleEmailsWithoutAuthentication() { Email email = inbox.waitForEmail(Duration.ofSeconds(10));
AuthResults auth = email.getAuthResults();
// Auth may be null for some email sources if (auth != null) { AuthValidation validation = auth.validate();
// Log results for debugging if (!validation.isFullyAuthenticated()) { System.out.println("Auth failures (expected for test emails): " + validation.getFailed()); } }}Testing Specific Checks
Section titled “Testing Specific Checks”class EmailAuthenticationTest { private Inbox inbox; private Email email;
@BeforeEach void setUp() { inbox = client.createInbox(); sendEmail(inbox.getEmailAddress()); email = inbox.waitForEmail(Duration.ofSeconds(10)); }
@AfterEach void tearDown() { inbox.delete(); }
@Test void shouldPassSpfCheck() { SpfResult spf = email.getAuthResults().getSpf(); if (spf != null) { assertThat(spf.getResult()).isIn("pass", "neutral", "softfail"); } }
@Test void shouldPassDkimCheck() { List<DkimResult> dkim = email.getAuthResults().getDkim(); if (dkim != null && !dkim.isEmpty()) { boolean anyPassed = dkim.stream() .anyMatch(d -> "pass".equalsIgnoreCase(d.getResult())); assertThat(anyPassed).isTrue(); } }
@Test void shouldPassDmarcCheck() { DmarcResult dmarc = email.getAuthResults().getDmarc(); if (dmarc != null) { assertThat(dmarc.getResult()).isIn("pass", "none"); } }}Why Authentication Matters
Section titled “Why Authentication Matters”Production Readiness
Section titled “Production Readiness”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
Real-World Example
Section titled “Real-World Example”@Testvoid shouldHaveProductionReadyEmailConfiguration() { app.sendWelcomeEmail(inbox.getEmailAddress());
Email email = inbox.waitForEmail(Duration.ofSeconds(10)); AuthValidation validation = email.getAuthResults().validate();
// In production, these should all pass if (!validation.isFullyAuthenticated()) { System.err.println("Email authentication issues detected:"); validation.getFailed().forEach(f -> System.err.println(" " + f)); System.err.println(); System.err.println("Action required:");
if (!validation.hasSpf()) { System.err.println("- Fix SPF record for your domain"); } if (!validation.hasDkim()) { System.err.println("- Configure DKIM signing in your email service"); } if (!validation.hasDmarc()) { System.err.println("- Add/fix DMARC policy"); } }
// Fail test if authentication fails assertThat(validation.isFullyAuthenticated()).isTrue();}Troubleshooting
Section titled “Troubleshooting”No Authentication Results
Section titled “No Authentication Results”AuthResults auth = email.getAuthResults();
if (auth == null) { System.out.println("No authentication results available"); System.out.println("This may happen for:"); System.out.println("- Emails sent from localhost/internal servers"); System.out.println("- Test SMTP servers without authentication"); return;}
if (auth.getSpf() == null && auth.getDkim() == null && auth.getDmarc() == null) { System.out.println("No authentication performed");}All Checks Fail
Section titled “All Checks Fail”AuthValidation validation = email.getAuthResults().validate();
if (!validation.isFullyAuthenticated()) { System.err.println("Authentication failed: " + validation.getFailed());
// 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}Understanding Failure Reasons
Section titled “Understanding Failure Reasons”AuthValidation validation = email.getAuthResults().validate();
for (String failure : validation.getFailed()) { if (failure.contains("SPF")) { System.out.println("Fix SPF: Update DNS TXT record for your domain"); } if (failure.contains("DKIM")) { System.out.println("Fix DKIM: Enable DKIM signing in your email service"); } if (failure.contains("DMARC")) { System.out.println("Fix DMARC: Add DMARC policy to DNS"); } if (failure.contains("ReverseDNS")) { System.out.println("Fix rDNS: Configure PTR record for mail server IP"); }}Debug Helper Method
Section titled “Debug Helper Method”void debugAuthResults(Email email) { AuthResults auth = email.getAuthResults(); if (auth == null) { System.out.println("No authentication results"); return; }
SpfResult spf = auth.getSpf(); System.out.println("SPF: " + (spf != null ? spf.getResult() : "N/A"));
List<DkimResult> dkim = auth.getDkim(); System.out.println("DKIM: " + (dkim != null && !dkim.isEmpty() ? dkim.get(0).getResult() : "N/A"));
DmarcResult dmarc = auth.getDmarc(); System.out.println("DMARC: " + (dmarc != null ? dmarc.getResult() : "N/A"));
ReverseDnsResult rdns = auth.getReverseDns(); System.out.println("rDNS: " + (rdns != null ? rdns.getResult() : "N/A"));
AuthValidation validation = auth.validate(); System.out.println("Passed: " + validation.getPassed()); System.out.println("Failed: " + validation.getFailed());}Next Steps
Section titled “Next Steps”- Email Authentication Guide - Testing authentication in depth
- Email Objects - Understanding email structure
- Testing Patterns - Real-world testing examples
- Gateway Security - Understanding the security model