npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

mailauth

v4.8.1

Published

Email authentication library for Node.js

Downloads

8,171

Readme

mailauth: Email Authentication for Node.js

mailauth Logo

mailauth is a comprehensive Node.js library and command-line utility for email authentication. It provides tools to work with various email security protocols, including SPF, DKIM, DMARC, ARC, BIMI, and MTA-STS. With mailauth, you can verify and sign emails, handle authentication results, and enhance your email security setup.

Key Features:

  • SPF verification
  • DKIM signing and verification
  • DMARC verification
  • ARC verification and sealing
    • Sealing during authentication
    • Sealing after message modifications
  • BIMI resolving and VMC validation
  • MTA-STS helper functions

mailauth is a pure JavaScript implementation, requiring no external applications or compilation. It runs on any server or device with Node.js version 16 or later.

Table of Contents

  1. Installation
  2. Command-Line Usage
  3. Library Usage
  4. Testing
  5. License

Installation

First, install mailauth from npm:

npm install mailauth

Then, import the desired methods into your script:

const { authenticate } = require('mailauth');

Command-Line Usage

mailauth includes a command-line utility called mailauth. For detailed information on how to use it, see the command-line documentation.

Library Usage

Authentication

Use the authenticate function to validate DKIM signatures, SPF, DMARC, ARC, and BIMI for an email.

Syntax

await authenticate(message [, options])
// Returns: { dkim, spf, arc, dmarc, bimi, receivedChain, headers }

Parameters

  • message: A String, Buffer, or Readable stream representing the email message.
  • options (optional):
    • sender (string): Email address from the MAIL FROM command. Defaults to the Return-Path header if not set.
    • ip (string): IP address of the remote client that sent the message.
    • helo (string): Hostname from the HELO/EHLO command.
    • trustReceived (boolean): If true, parses ip and helo from the latest Received header if not provided. Defaults to false.
    • mta (string): Hostname of the server performing the authentication. Defaults to os.hostname(). Included in Authentication headers.
    • minBitLength (number): Minimum allowed bits for RSA public keys. Defaults to 1024. Keys with fewer bits will fail validation.
    • disableArc (boolean): If true, skips ARC checks.
    • disableDmarc (boolean): If true, skips DMARC checks, also disabling dependent checks like BIMI.
    • disableBimi (boolean): If true, skips BIMI checks.
    • seal (object): Options for ARC sealing if the message doesn't have a broken ARC chain.
      • signingDomain (string): ARC key domain name.
      • selector (string): ARC key selector.
      • privateKey (string or Buffer): Private key for signing (RSA or Ed25519).
    • resolver (async function): Custom DNS resolver function. Defaults to dns.promises.resolve.
    • maxResolveCount (number): DNS lookup limit for SPF. Defaults to 10 as per RFC7208.
    • maxVoidCount (number): DNS lookup limit for SPF producing empty results. Defaults to 2 as per RFC7208.

Example

const { authenticate } = require('mailauth');
const dns = require('dns');

const message = /* Your email message here */;

const { dkim, spf, arc, dmarc, bimi, receivedChain, headers } = await authenticate(message, {
  // SMTP transmission options
  ip: '217.146.67.33',                 // SMTP client IP
  helo: 'uvn-67-33.tll01.zonevs.eu',   // HELO/EHLO hostname
  sender: '[email protected]',           // MAIL FROM address

  // Uncomment to parse `ip` and `helo` from the latest `Received` header
  // trustReceived: true,

  // Server performing the authentication
  mta: 'mx.ethereal.email',

  // Optional DNS resolver function
  resolver: async (name, rr) => await dns.promises.resolve(name, rr),
});

// Output authenticated message
process.stdout.write(headers); // Includes terminating line break
process.stdout.write(message);

Sample Output:

Received-SPF: pass (mx.ethereal.email: domain of [email protected] designates 217.146.67.33 as permitted sender) client-ip=217.146.67.33;
Authentication-Results: mx.ethereal.email;
 dkim=pass [email protected] header.s=default header.a=rsa-sha256 header.b=TXuCNlsq;
 spf=pass (mx.ethereal.email: domain of [email protected] designates 217.146.67.33 as permitted sender) [email protected]
 smtp.helo=uvn-67-33.tll01.zonevs.eu;
 arc=pass (i=2 spf=neutral dkim=pass dkdomain=ekiri.ee);
 dmarc=none header.from=ekiri.ee
From: ...

You can see the full output, including structured data for DKIM, SPF, DMARC, and ARC, from this example.

Note: The receivedChain property is an array of parsed representations of the Received: headers.

DKIM

DKIM Signing

Use the dkimSign function to sign an email message with DKIM.

Syntax
const { dkimSign } = require('mailauth/lib/dkim/sign');

const signResult = await dkimSign(message, options);
// Returns: { signatures: String, errors: Array }
Parameters
  • message: A String, Buffer, or Readable stream representing the email message.
  • options:
    • canonicalization (string): Canonicalization method. Defaults to 'relaxed/relaxed'.
    • algorithm (string): Signing and hashing algorithm. Defaults to 'rsa-sha256'.
    • signTime (Date): Signing time. Defaults to current time.
    • signatureData (Array): Array of signature objects. Each object may contain:
      • signingDomain (string): DKIM key domain name.
      • selector (string): DKIM key selector.
      • privateKey (string or Buffer): Private key for signing (RSA or Ed25519).
      • algorithm (string, optional): Overrides parent algorithm.
      • canonicalization (string, optional): Overrides parent canonicalization.
      • maxBodyLength (number, optional): Maximum number of canonicalized body bytes to sign (l= tag). Not recommended for general use.
Example
const { dkimSign } = require('mailauth/lib/dkim/sign');
const fs = require('fs');

const message = /* Your email message here */;

const signResult = await dkimSign(message, {
  canonicalization: 'relaxed/relaxed',
  algorithm: 'rsa-sha256',
  signTime: new Date(),
  signatureData: [
    {
      signingDomain: 'tahvel.info',
      selector: 'test.rsa',
      privateKey: fs.readFileSync('./test/fixtures/private-rsa.pem'),
    },
  ],
});

// Display signing errors if any
if (signResult.errors.length) {
  console.error('Signing errors:', signResult.errors);
}

// Output signed message
process.stdout.write(signResult.signatures); // Includes terminating line break
process.stdout.write(message);

Sample Output:

DKIM-Signature: a=rsa-sha256; v=1; c=relaxed/relaxed; d=tahvel.info;
 s=test.rsa; b=...
From: ...

DKIM Signing as a Stream

Use DkimSignStream to sign messages as part of a stream processing pipeline.

Example
const { DkimSignStream } = require('mailauth/lib/dkim/sign');
const fs = require('fs');

const dkimSignStream = new DkimSignStream({
    canonicalization: 'relaxed/relaxed',
    algorithm: 'rsa-sha256',
    signTime: new Date(),
    signatureData: [
        {
            signingDomain: 'tahvel.info',
            selector: 'test.rsa',
            privateKey: fs.readFileSync('./test/fixtures/private-rsa.pem')
        }
    ]
});

// Read from stdin, write signed message to stdout
process.stdin.pipe(dkimSignStream).pipe(process.stdout);

DKIM Verification

Use the dkimVerify function to verify DKIM signatures in an email message.

Syntax
const { dkimVerify } = require('mailauth/lib/dkim/verify');

const result = await dkimVerify(message);
// Returns an object containing verification results
Example
const { dkimVerify } = require('mailauth/lib/dkim/verify');

const message = /* Your email message here */;

const result = await dkimVerify(message);

for (const { info } of result.results) {
  console.log(info);
}

Sample Output:

dkim=neutral (invalid public key) [email protected] header.s=test.invalid header.b="b85yao+1"
dkim=pass [email protected] header.s=test.rsa header.b="BrEgDN4A"
dkim=policy policy.dkim-rules=weak-key [email protected] header.s=test.small header.b="d0jjgPun"

SPF

SPF Verification

Use the spf function to verify the SPF record for an email sender.

Syntax
const { spf } = require('mailauth/lib/spf');

const result = await spf(options);
// Returns an object containing SPF verification results
Parameters
  • options:
    • sender (string): MAIL FROM address.
    • ip (string): SMTP client IP.
    • helo (string): HELO/EHLO hostname.
    • mta (string): Hostname of the MTA performing the check.
Example
const { spf } = require('mailauth/lib/spf');

const result = await spf({
    sender: '[email protected]',
    ip: '217.146.76.20',
    helo: 'foo',
    mta: 'mx.myhost.com'
});

console.log(result.header);

Sample Output:

Received-SPF: pass (mx.myhost.com: domain of [email protected]
 designates 217.146.76.20 as permitted sender) client-ip=217.146.76.20;
 envelope-from="[email protected]";

ARC

ARC Validation

ARC seals are validated automatically during the authentication step.

Example
const { authenticate } = require('mailauth');

const message = /* Your email message here */;

const { arc } = await authenticate(message, {
  trustReceived: true,
});

console.log(arc);

Sample Output:

{
    "status": {
        "result": "pass",
        "comment": "i=2 spf=neutral dkim=pass dkdomain=zonevs.eu dkim=pass dkdomain=srs3.zonevs.eu dmarc=fail fromdomain=zone.ee"
    },
    "i": 2
    // Additional properties...
}

ARC Sealing

You can seal messages with ARC either during authentication or after modifications.

Sealing During Authentication

Provide the sealing key in the options to seal messages automatically during authentication.

const { authenticate } = require('mailauth');
const fs = require('fs');

const message = /* Your email message here */;

const { headers } = await authenticate(message, {
  trustReceived: true,
  seal: {
    signingDomain: 'tahvel.info',
    selector: 'test.rsa',
    privateKey: fs.readFileSync('./test/fixtures/private-rsa.pem'),
  },
});

// Output authenticated and sealed message
process.stdout.write(headers); // Includes terminating line break
process.stdout.write(message);
Sealing After Modifications

If you need to modify the message before sealing, first authenticate it, modify as needed, then seal using the authentication results.

const { authenticate, sealMessage } = require('mailauth');
const fs = require('fs');

const message = /* Your email message here */;

// Step 1: Authenticate the message
const { arc, headers } = await authenticate(message, {
  ip: '217.146.67.33',
  helo: 'uvn-67-33.tll01.zonevs.eu',
  mta: 'mx.ethereal.email',
  sender: '[email protected]',
});

// Step 2: Modify the message as needed
// ... your modifications ...

// Step 3: Seal the modified message
const sealHeaders = await sealMessage(message, {
  signingDomain: 'tahvel.info',
  selector: 'test.rsa',
  privateKey: fs.readFileSync('./test/fixtures/private-rsa.pem'),
  authResults: arc.authResults,
  cv: arc.status.result,
});

// Output the sealed message
process.stdout.write(sealHeaders); // ARC headers
process.stdout.write(headers);     // Authentication results
process.stdout.write(message);

DMARC

DMARC is verified during the authentication process. Although the dmarc handler is exported, it requires input from previous steps like SPF and DKIM.

DMARC Helpers

getDmarcRecord(domain [, resolver])

Fetches and parses the DMARC DNS record for a domain or subdomain. Returns false if no record exists.

Syntax
const getDmarcRecord = require('mailauth/lib/dmarc/get-dmarc-record');

const dmarcRecord = await getDmarcRecord(domain [, resolver]);
// Returns an object with DMARC record details or `false` if not found
Parameters
  • domain (string): The domain to check for a DMARC record.
  • resolver (function, optional): Custom DNS resolver function. Defaults to dns.resolve.
Example
const getDmarcRecord = require('mailauth/lib/dmarc/get-dmarc-record');

const dmarcRecord = await getDmarcRecord('ethereal.email');
console.log(dmarcRecord);

Sample Output:

{
    "v": "DMARC1",
    "p": "none",
    "pct": 100,
    "rua": "mailto:[email protected]",
    "sp": "none",
    "aspf": "r",
    "rr": "v=DMARC1; p=none; pct=100; rua=mailto:[email protected]; sp=none; aspf=r;",
    "isOrgRecord": false
}

BIMI

Brand Indicators for Message Identification (BIMI) support is based on draft-blank-ietf-bimi-02. BIMI information is resolved during the authentication step, provided the message passes DMARC validation with a policy other than "none".

Example

const { authenticate } = require('mailauth');

const message = /* Your email message here */;

const { bimi } = await authenticate(message, {
  ip: '217.146.67.33',
  helo: 'uvn-67-33.tll01.zonevs.eu',
  mta: 'mx.ethereal.email',
  sender: '[email protected]',
  bimiWithAlignedDkim: false, // If true, ignores SPF in DMARC and requires a valid DKIM signature
});

if (bimi?.location) {
  console.log(`BIMI location: ${bimi.location}`);
}

Note:

  • The BIMI-Location header is ignored by mailauth.
  • The BIMI-Selector header can be used for selector selection if available.

Verified Mark Certificate (VMC)

If an Authority Evidence Document is specified in the BIMI record, its location is available in bimi.authority. mailauth exposes the certificate type ("VMC" or "CMC") in bimi.authority.vmc.type.

Example Authority Evidence Documents:

MTA-STS

mailauth provides functions to fetch and validate MTA-STS policies for a domain.

Policy Retrieval

Use the getPolicy function to fetch the MTA-STS policy for a domain.

Syntax
const { getPolicy } = require('mailauth/lib/mta-sts');

const { policy, status } = await getPolicy(domain [, knownPolicy]);
// Returns an object with the policy and status
Parameters
  • domain (string): The domain to retrieve the policy for.
  • knownPolicy (object, optional): Previously cached policy for the domain.
Example
const { getPolicy } = require('mailauth/lib/mta-sts');

const knownPolicy = /* Retrieve from your cache if available */;
const { policy, status } = await getPolicy('gmail.com', knownPolicy);

if (policy.id !== knownPolicy?.id) {
  // Update your cache with the new policy
}

if (policy.mode === 'enforce') {
  // TLS must be used when sending to this domain
}

Possible Status Values:

  • "not_found": No policy was found.
  • "cached": Existing policy is still valid.
  • "found": New or updated policy found.
  • "renew": Existing policy is valid; renew cache.
  • "errored": Policy discovery failed due to a temporary error.

MX Validation

Use the validateMx function to check if an MX hostname is valid according to the MTA-STS policy.

Syntax
const { validateMx } = require('mailauth/lib/mta-sts');

const validation = validateMx(mx, policy);
// Returns an object indicating if the MX is valid
Parameters
  • mx (string): The resolved MX hostname.
  • policy (object): The MTA-STS policy object.
Example
const { getPolicy, validateMx } = require('mailauth/lib/mta-sts');

const { policy } = await getPolicy('gmail.com');

const mx = 'alt4.gmail-smtp-in.l.google.com';
const policyMatch = validateMx(mx, policy);

if (policy.mx && !policyMatch.valid) {
    // The MX host is not listed in the policy; do not connect
}

Testing

mailauth uses the following test suites:

SPF Test Suite

Based on the OpenSPF test suite, with some differences:

  • Less strict whitespace checks.
  • Some macro tests are skipped.
  • Some tests are skipped where the invalid component is after a matching part.
  • All other tests pass.

ARC Test Suite from ValiMail

Based on ValiMail's arc_test_suite:

  • mailauth is less strict on header tags and casing.
  • Signing test suite is used for input; mailauth validates signatures and checks for the same cv= output.
  • All tests pass, aside from minor differences.

License

© 2020-2024 Postal Systems OÜ

Licensed under the MIT License.