FlexiMail 0.2.0
See the version list below for details.
dotnet add package FlexiMail --version 0.2.0
NuGet\Install-Package FlexiMail -Version 0.2.0
<PackageReference Include="FlexiMail" Version="0.2.0" />
<PackageVersion Include="FlexiMail" Version="0.2.0" />
<PackageReference Include="FlexiMail" />
paket add FlexiMail --version 0.2.0
#r "nuget: FlexiMail, 0.2.0"
#:package FlexiMail@0.2.0
#addin nuget:?package=FlexiMail&version=0.2.0
#tool nuget:?package=FlexiMail&version=0.2.0
<p align="center"> <img src="https://github.com/mabroukmahdhi/FlexiMail/blob/main/FlexiMail/icmail.png" alt="FlexiMail logo"> </p>
FlexiMail
FlexiMail is a test-driven email client for .NET 8 and C# 12 that now supports both Exchange (EWS) and Microsoft Graph through the new FlexiGraphService.
Features
- Exchange and Microsoft Graph mail sending with sent-items copy
- Microsoft Graph inbox reading, including bodies and file attachments
- Microsoft Graph webhook subscription management for new inbox messages
FlexiGraphServicefor Graph-based delivery- Asynchronous APIs
- Test-first design with unit and integration coverage
Installation
dotnet add package FlexiMail
# or
Install-Package FlexiMail
Usage
Note: The Exchange constructor of
FlexiMailClientis compiled only fornet8.0andnet9.0. When targetingnet10.0, use the Graph constructor (FlexiMailClient(GraphMailConfigurations)).
Send via Exchange (EWS)
using FlexiMail;
using FlexiMail.Models.Configurations;
using FlexiMail.Models.Foundations.Bodies;
using FlexiMail.Models.Foundations.Messages;
var configurations = new ExchangeConfigurations
{
ClientId = "your-client-id",
ClientSecret = "your-client-secret",
TenantId = "your-tenant-id",
Authority = "https://login.microsoftonline.com/{tenantId}",
Scopes = ["https://outlook.office365.com/.default"],
SmtpAddress = "sender@domain.com"
};
var client = new FlexiMailClient(configurations);
await client.SendAndSaveCopyAsync(new FlexiMessage
{
To = ["email@domain.com"],
Subject = "Hello from FlexiMail",
Body = new FlexiBody
{
Content = "This is the message body.",
ContentType = BodyContentType.PlainText
}
});
Read received email with Microsoft Graph
Inbound APIs require a Graph-configured client. When mailbox is omitted,
SenderUserIdOrUpn is used.
var page = await client.GetInboxAsync(
mailbox: "support@domain.com",
pageSize: 50,
unreadOnly: true);
foreach (var summary in page.Messages)
{
var message = await client.GetReceivedMessageAsync(
messageId: summary.Id,
mailbox: "support@domain.com");
Console.WriteLine($"{message.ReceivedDateTime}: {message.From} - {message.Subject}");
Console.WriteLine(message.Body?.Content);
}
GetInboxAsync requests newest messages first when reading all messages. Graph
does not guarantee ordering when the unread-only filter is used. pageSize must
be between 1 and 1000. GetReceivedMessageAsync also expands file attachments
and returns their content in FlexiAttachment.Bytes.
Receive notifications for new email
The application must expose a publicly accessible HTTPS webhook. FlexiMail creates and manages the Microsoft Graph subscription; the consuming ASP.NET Core application hosts the endpoint.
const string clientState = "store-this-as-a-secret";
var subscription = await client.SubscribeToInboxAsync(
notificationUrl: "https://api.domain.com/webhooks/fleximail",
clientState: clientState,
mailbox: "support@domain.com",
lifecycleNotificationUrl: "https://api.domain.com/webhooks/fleximail/lifecycle");
// Persist subscription.Id and subscription.ExpirationDateTime.
// Renew it before expiration:
subscription = await client.RenewSubscriptionAsync(subscription.Id);
// Remove it when no longer required:
await client.DeleteSubscriptionAsync(subscription.Id);
Minimal API webhook example:
using FlexiMail.Models.Subscriptions;
using System.Text.Json;
app.MapPost("/webhooks/fleximail", async (
HttpRequest request,
CancellationToken cancellationToken) =>
{
// Microsoft Graph validates the URL while the subscription is created.
if (request.Query.TryGetValue("validationToken", out var token))
{
return Results.Text(token.ToString(), "text/plain");
}
var notifications = await JsonSerializer.DeserializeAsync<FlexiMailNotificationCollection>(
request.Body,
cancellationToken: cancellationToken);
foreach (var notification in notifications?.Value ?? [])
{
if (!notification.HasClientState(clientState))
{
continue;
}
// Queue this work in production and return promptly.
var message = await client.GetReceivedMessageAsync(
notification.ResourceData.Id,
mailbox: "support@domain.com",
cancellationToken);
// Process message here.
}
return Results.Accepted();
});
Graph validates a webhook by POSTing a validationToken query parameter. The
endpoint must return its URL-decoded value as text/plain within 10 seconds.
For normal notifications, validate clientState, enqueue processing, and return
202 Accepted quickly. Subscriptions created by FlexiMail last six days and
must be renewed. Lifecycle notifications and durable subscription storage are
recommended for production.
The Entra application needs the Microsoft Graph application permission
Mail.Read with administrator consent. Because this permission can read tenant
mailboxes, administrators should restrict the application's mailbox access in
Exchange Online where appropriate. Mail.Send remains required for sending.
Send via Microsoft Graph
using FlexiMail;
using FlexiMail.Models.Configurations;
using FlexiMail.Models.Foundations.Bodies;
using FlexiMail.Models.Foundations.Messages;
var configurations = new GraphMailConfigurations
{
ClientId = "your-client-id",
ClientSecret = "your-client-secret",
TenantId = "your-tenant-id",
SenderUserIdOrUpn = "sender@domain.com",
Scopes = ["https://graph.microsoft.com/.default"]
};
var client = new FlexiMailClient(configurations);
await client.SendAndSaveCopyAsync(new FlexiMessage
{
To = ["email@domain.com"],
Subject = "Hello from FlexiGraphService",
Body = new FlexiBody
{
Content = "Graph-powered delivery.",
ContentType = BodyContentType.Html
}
});
Configuration
Example appsettings.json snippet:
{
"ExchangeConfigurations": {
"ClientId": "your-client-id",
"ClientSecret": "your-client-secret",
"TenantId": "your-tenant-id",
"SmtpAddress": "sender@domain.com",
"Authority": "https://login.microsoftonline.com/{tenantId}",
"Scopes": ["https://outlook.office365.com/.default"]
},
"GraphMailConfigurations": {
"ClientId": "your-client-id",
"ClientSecret": "your-client-secret",
"TenantId": "your-tenant-id",
"SenderUserIdOrUpn": "sender@domain.com",
"Scopes": ["https://graph.microsoft.com/.default"]
}
}
The Scopes value remains https://graph.microsoft.com/.default; actual
permissions (Mail.Send, Mail.Read) are configured and consented on the Entra
application registration.
Architecture
- Brokers: integrations with Exchange and Graph
- Services: core workflows, including
FlexiGraphServicefor Graph - Models: message, body, and configuration contracts
FlexiMailClient chooses the appropriate service based on the provided
configuration and always saves a copy to Sent Items. Inbox reading and
subscription management are Graph-only; calling them on an Exchange-configured
client throws NotSupportedException.
Contributing
- Fork the repository
- Create a branch (
git checkout -b users/your-github-id/feature-name) - Commit (
git commit -m "Add feature") - Push (
git push origin users/your-github-id/feature-name) - Open a Pull Request
License
MIT. See LICENSE.
Contact
For questions: contact@mahdhi.com
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Azure.Identity (>= 1.17.1)
- Microsoft.Extensions.DependencyInjection (>= 10.0.2)
- Microsoft.Graph (>= 5.101.0)
- Xeption (>= 2.8.0)
-
net8.0
- Azure.Identity (>= 1.17.1)
- Microsoft.Exchange.WebServices (>= 2.2.0)
- Microsoft.Extensions.DependencyInjection (>= 10.0.2)
- Microsoft.Graph (>= 5.101.0)
- Xeption (>= 2.8.0)
-
net9.0
- Azure.Identity (>= 1.17.1)
- Microsoft.Exchange.WebServices (>= 2.2.0)
- Microsoft.Extensions.DependencyInjection (>= 10.0.2)
- Microsoft.Graph (>= 5.101.0)
- Xeption (>= 2.8.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Added Microsoft Graph inbox reading and individual message retrieval with file attachments. Added new-message webhook subscription creation, renewal, deletion, notification contracts, and client-state validation. Added .NET 8, .NET 9, and .NET 10 documentation and examples for inbound mail.