Skip to main content

Notifications & Templates

nauth-toolkit sends emails and SMS messages automatically during authentication flows --- verification codes, password resets, MFA challenges, and security alerts. Both systems use Handlebars templates that you can override with your own branding and wording.

How It Works

  1. A core service (signup, password reset, MFA, etc.) triggers a notification
  2. The appropriate provider (email or SMS) receives event-specific variables (code, link, expiry, etc.)
  3. The template engine merges global variables (branding) with event variables
  4. Handlebars renders the template
  5. The message is sent via your configured transport

@nauth-toolkit/email-nodemailer ships with production-ready responsive HTML templates for all 18 email types. SMS ships with 3 default templates. You only need to override the ones you want to customize.

Email Templates

Template Types

Core authentication emails

These emails are sent automatically during authentication flows. They cannot be suppressed individually (except via the global kill switch emailNotifications.enabled: false).

Template TypeWhen SentRequired Variables
verificationEmail verification during signupcode, link, expiryMinutes
mfaEmailCodeEmail MFA challenge codecode, expiryMinutes
passwordResetUser requests password resetlink, expiryMinutes
adminPasswordResetAdmin initiates password resetcode, link, expiryMinutes
welcomeAfter onboarding completes (post-verification or immediately if verification is disabled)(none)
Understanding "required variables"

The validator checks that your template mentions each variable (e.g. {{code}} or {{#if code}}). At runtime, some variables may be undefined --- for example, link is only provided when a baseUrl is configured. Use Handlebars conditionals to handle this:

{{#if link}}<a href="{{link}}">Reset Password</a>{{/if}}

Security notification emails

These emails are suppressed by default. Enable them individually via emailNotifications.suppress:

{
emailNotifications: {
enabled: true,
suppress: {
passwordChanged: false, // Enable password changed notifications
accountLockout: false, // Enable lockout notifications
sessionsRevoked: false, // Enable session revocation notifications
},
},
}
Template TypeWhen SentRequired Variables
passwordChangedPassword successfully changed(none)
accountLockoutAccount locked (failed login attempts)reason, durationMinutes
newDeviceNew device detected during logindeviceName, timestamp
sessionsRevokedAll sessions terminatedrevokedCount
adaptiveMfaRiskAlertAdaptive MFA risk detected with notifyUser: trueriskLevel, riskFactors

Account management emails

Template TypeWhen SentRequired Variables
accountDisabledAccount disabled by adminreason
accountEnabledAccount re-enabled by admin(none)
emailChangedOldEmail changed (sent to old address)(none)
emailChangedNewEmail changed (sent to new address)(none)
emailChangedEmail changed (legacy single-email template)userEmail

MFA notification emails

Template TypeWhen SentRequired Variables
mfaEnabledFirst MFA method enabled(none)
mfaDeviceRemovedMFA device removed(none)
mfaMethodAddedAdditional MFA method added(none)

Suppression Keys vs Template Keys

The emailNotifications.suppress keys do not always match the TemplateType keys:

Suppress KeyTemplate Type
mfaFirstEnabledmfaEnabled
adaptiveMfaRiskDetectedadaptiveMfaRiskAlert
emailChangedOldemailChangedOld
emailChangedNewemailChangedNew
accountLockoutaccountLockout

Use template type keys for customTemplates and suppress keys for emailNotifications.suppress.

Email Template Variables

Global variables (available to all templates)

VariableSourceDescription
appNameGlobalApplication name
companyNameGlobalCompany name
companyAddressGlobalCompany address
supportEmailGlobalSupport email
brandColorGlobalBrand hex color
logoUrlGlobalLogo URL
dashboardUrlGlobalDashboard URL
footerDisclaimerGlobalCustom footer text
currentYearAutoCurrent year (e.g. 2026)
subjectAutoRendered subject (available inside HTML/text body)
previewTextAutoEmail preview text (defaults to subject)
userNameAutoDerived from email prefix (e.g. john from john@example.com)
userEmailAutoRecipient email address

Event-specific variables

Template TypeVariablesNotes
verificationcode, link, expiryMinuteslink only present when signup.emailVerification.baseUrl is configured
mfaEmailCodecode, expiryMinutes
passwordResetcode, link, expiryMinuteslink only present when baseUrl is provided in the forgotPassword request
adminPasswordResetcode, link, expiryMinuteslink only present when baseUrl is provided
welcome(none)Uses global variables only
accountLockoutreason, durationMinutes, lockType, lockDuration, lockedUntil, ipAddress, failedAttemptslockType is 'temporary' or 'permanent'
newDevicedeviceName, deviceType, ipAddress, location, timestamp
passwordChangedchangedBy, sessionsRevoked, timestampchangedBy is 'user', 'admin', or 'reset'
sessionsRevokedrevokedCount, reason, triggerEvent, timestamp
adaptiveMfaRiskAlertriskScore, riskLevel, riskFactors, action, timestampriskLevel is 'low', 'medium', or 'high'
accountDisabledreason, performedBy, timestamp
accountEnabledreason, performedBy, timestamp
emailChangedOldnewEmail, deactivatedMFADevices, timestamp, isOldEmailSent to the old email
emailChangedNewoldEmail, timestamp, isOldEmailSent to the new email
mfaEnabledfirstMethod, deviceName, timestamp
mfaDeviceRemoveddeviceType, deviceName, removedBy, reason, remainingDeviceCountremovedBy is 'user', 'admin', or 'system'
mfaMethodAddedmethod, enabledMethods, deviceName, timestampenabledMethods is an array

The password reset flow always sends a code. The link is only included when baseUrl is provided in the request. Your template should handle both scenarios:

{{#if link}}
<p><a href="{{link}}">Reset Password</a></p>
<p>Or enter this code manually: <strong>{{code}}</strong></p>
{{else}}
<p>Your password reset code: <strong>{{code}}</strong></p>
{{/if}}
<p>This code expires in {{expiryMinutes}} minutes.</p>

This pattern applies equally to verification and adminPasswordReset templates.

SMS Templates

Template Types

Template TypeWhen SentRequired Variables
verificationPhone verification during signup or phone number updatecode, expiryMinutes
mfaMFA code for two-factor authentication via SMScode, expiryMinutes
passwordResetPassword reset via SMS (when configured for SMS delivery)code, expiryMinutes
note

The template type is determined automatically by the core service:

  • Phone verification sends verification (or mfa if the phone is already verified and the check is for MFA)
  • SMS MFA challenge sends mfa
  • Password reset via SMS sends passwordReset

Default SMS Templates

If you don't override any templates, these defaults are used:

verification:

{{#if appName}}{{appName}}: {{/if}}Your verification code is: {{code}}. Valid for {{expiryMinutes}} minutes.

mfa:

{{#if appName}}{{appName}}: {{/if}}Your MFA code is: {{code}}. Valid for {{expiryMinutes}} minutes.

passwordReset:

{{#if appName}}{{appName}}: {{/if}}Your password reset code is: {{code}}. Valid for {{expiryMinutes}} minutes.

SMS Template Variables

VariableSourceDescriptionExample
codeEventVerification/MFA/reset code123456
expiryMinutesEventCode expiration time in minutes5
appNameGlobalApplication nameMy App
firstNameEventUser's first nameJohn
lastNameEventUser's last nameDoe
userNameEventUsernamejohn_doe
userEmailEventEmail addressjohn@example.com
phoneEventPhone number (E.164)+1234567890
companyNameGlobalCompany nameMy Company Inc.
supportPhoneGlobalSupport phone number+1-800-123-4567
note

You can add any additional custom variables to globalVariables. They will be available in all templates via {{variableName}}.

Handlebars Syntax Reference

Email templates (full syntax)

Email templates support the full Handlebars feature set:

Variables:

<p>Welcome to {{appName}}!</p>
<p>Your code is: {{code}}</p>
<p>&copy; {{currentYear}} {{companyName}}</p>

Conditionals:

{{#if firstName}}
<p>Hello {{firstName}}!</p>
{{else}}
<p>Hello!</p>
{{/if}}

{{#unless isVerified}}
<p>Please verify your email.</p>
{{/unless}}

Loops:

{{#each riskFactors}}
<li>{{this}}</li>
{{/each}}

Built-in helpers:

{{#if (eq changedBy "admin")}}
<p>An administrator changed your password.</p>
{{/if}}

{{#if (gt riskScore 80)}}
<p>High risk detected!</p>
{{/if}}

<p>Date: {{formatDate timestamp}}</p>
HelperDescription
eqEqual: (eq a b)
neNot equal: (ne a b)
gtGreater than: (gt a b)
ltLess than: (lt a b)
andLogical AND: (and a b)
orLogical OR: (or a b)
formatDateFormat date: {{formatDate timestamp}}

Greeting pattern:

{{#if firstName}}
<p>Hi {{firstName}},</p>
{{else}}
{{#if userName}}<p>Hi {{userName}},</p>{{else}}<p>Hi,</p>{{/if}}
{{/if}}

SMS templates (subset only)

SMS templates use a lightweight Handlebars-like engine that supports {{variable}} and {{#if variable}}...{{/if}} only.

{{#if appName}}{{appName}}: {{/if}}Your code is {{code}}.
warning

Advanced Handlebars features ({{#each}}, {{#unless}}, {{#with}}, nested helpers) are not supported in SMS templates. Use email templates for complex logic.

What's Next