When sending outbound emails through Microsoft Graph (Exchange Online), we want to guarantee that:
An inbound email is a legitimate reply to the exact outbound message we sent — not a spoofed or fabricated email.
Traditional email headers (From, In-Reply-To, References) can be forged.
DKIM/SPF/DMARC validate domain authenticity — but do not cryptographically bind an inbound reply to a specific outbound message.
Microsoft Graph does not provide a built-in feature that verifies “this inbound message is a guaranteed reply to outbound message X.”
Therefore, we implement our own tamper-evident reply binding mechanism.
Use:
- Exchange Online Plus Addressing
- Signed reply tokens
- Graph sendMail (with replyTo)
- Graph delta query for inbound processing
This creates a cryptographically verifiable link between outbound and inbound messages.
Internal API
↓
Microsoft Graph sendMail
↓
Exchange Online
↓
Client replies
↓
Shared Mailbox (replies@contoso.com)
↓
Inbound Processor (Graph delta query)
↓
Token validation + outbound binding
Instead of trusting email headers, we embed a cryptographically signed token into the replyTo address using plus addressing.
From: notifications@contoso.com
Reply-To: replies+<signedToken>@contoso.com
When the client clicks “Reply,” the message is sent to:
replies+<signedToken>@contoso.com
Because Exchange Online supports plus addressing, the email is delivered to:
replies@contoso.com
But the token remains visible in the recipient address for validation.
{
"version": 1,
"outboundId": "6f2d4cbb-0a62-4fcb-b4a5-1f5e44b6b112",
"recipientHash": "sha256(email)",
"tenantId": "enterprise-01",
"issuedAt": 1700000000,
"expiresAt": 1700003600,
"nonce": "random-128-bit"
}Recommended:
- HMAC-SHA256 (shared secret)
- or Ed25519 (public/private key pair)
Then encode:
Base64UrlEncode(payload + signature)
The resulting token must be URL-safe (no +, /, or = characters).
- Create
outboundId - Store outbound record in database
- Create token payload
- Sign token
Example JSON body for POST /users/{id}/sendMail:
{
"message": {
"subject": "Your Case Update",
"body": {
"contentType": "HTML",
"content": "<p>Your case has been updated.</p>"
},
"toRecipients": [
{
"emailAddress": {
"address": "client@example.com"
}
}
],
"replyTo": [
{
"emailAddress": {
"address": "replies+SIGNED_TOKEN@contoso.com"
}
}
]
}
}Microsoft Graph supports setting replyTo on messages.
Monitor the shared mailbox:
GET /users/replies@contoso.com/mailFolders('Inbox')/messages/delta
For each new message:
-
Extract recipient address
-
Parse token from
replies+<token>@contoso.com -
Decode and validate token
-
Verify:
- Signature valid
- Not expired
- OutboundId exists
- Nonce unused (optional replay protection)
-
Bind inbound message to outbound record
If validation fails → mark as Unverified Reply
An inbound message is marked as Verified Reply only if:
- Token is present
- Signature is valid
- Token not expired
- Token maps to stored outbound record
- (Optional) inbound
Frommatches expected recipient - (Optional) nonce not previously used
As a secondary safeguard, embed a reference line in the body:
Reference: ACME-REPLY:v1.<token>
On inbound:
- Scan body for token
- Validate same way
This protects against scenarios where users manually edit the recipient address.
| Mechanism | Protects Against Spoofing? | Binds to Specific Outbound? |
|---|---|---|
| In-Reply-To | ❌ No | ❌ No |
| References | ❌ No | ❌ No |
| DKIM/SPF/DMARC | ✅ Domain validation | ❌ Not per-message binding |
| Custom Headers | ⚠ Sometimes preserved | ❌ Not reliable |
| Signed Reply Token | ✅ Yes | ✅ Yes |
- Use key rotation (include
kidin token header) - Store token hash instead of full token
- Enforce short expiration window
- Enable replay detection
- Log validation events
- Rate limit per token
- Use tenant-scoped signing keys
- Monitor plus addressing configuration in Exchange Online
With this approach:
✔ You can prove the reply channel was minted by your system ✔ You can prove it references a specific outbound message ✔ Spoofed emails cannot generate valid tokens ✔ Forwarded replies still validate ✔ Validation is deterministic and auditable
- No built-in “verified reply to this outbound” flag
- No automatic cryptographic reply binding
- No built-in anti-reply spoofing mechanism
Graph provides:
replyTo- Custom headers (limited use)
- Message delta queries
- Mailbox monitoring
The cryptographic binding must be implemented in your application layer.
In an enterprise environment using Microsoft Graph:
The correct pattern for tamper-evident reply validation is:
Signed reply tokens embedded in
replyTousing Exchange Online plus addressing, validated on inbound via Graph.
This provides strong cryptographic assurance that an inbound email is a legitimate reply to a specific outbound message sent by your system.
If desired, this document can be extended with:
- C# sample token generation/validation code
- Ed25519 implementation example
- Azure Key Vault key management pattern
- Multi-tenant architecture variant