This Azure Function App provides a centralized email sending service that can be consumed by various applications using JWT-based authentication. It includes:
SendEmailFunction: Accepts email payloads and sends emailsGetTokenFunction: Authenticates client apps and issues JWT tokens- Built-in validation against a database table for allowed client apps
- Centralized email dispatch service
- JWT-based authentication
- Request validation with client AppName and API key
- Database logging of email requests
- Retry-safe design for resilience
- Easily consumable by .NET (C#/VB.NET), Python, etc.
- .NET 6 or later
- Azure CLI or Azure Functions Core Tools
- Azure Storage Account (for function app)
- SQL Database (for app authentication and logging)
- SMTP credentials or integration with SendGrid
- Clone this repository
- Set up local settings:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"JwtSecret": "<your-secret>",
"ConnectionStrings:DefaultConnection": "<your-sql-connection-string>"
}
}- Add SQL Tables
CREATE TABLE AllowedApps (
Id INT PRIMARY KEY IDENTITY,
AppName NVARCHAR(100) NOT NULL,
ApiKey NVARCHAR(200) NOT NULL,
IsActive BIT NOT NULL
);
CREATE TABLE EmailLog (
Id INT PRIMARY KEY IDENTITY,
AppName NVARCHAR(100),
MailTo NVARCHAR(200),
MailFrom NVARCHAR(200),
Subject NVARCHAR(500),
Body NVARCHAR(MAX),
Priority NVARCHAR(20),
Status NVARCHAR(20),
RetryCount INT,
CreatedAt DATETIME DEFAULT GETDATE()
);- The client app sends a POST request to /api/gettoken with its AppName and ApiKey
- The function returns a JWT if the credentials match an active app in the database
- The client must include the token in the Authorization header as Bearer when calling /api/SendEmail
Request Body:
{
"AppName": "ClientApp1",
"ApiKey": "your-api-key"
}
Response:
{
"token": "<jwt-token>"
}
Authorization: Bearer <jwt-token>{
"AppName": "ClientApp1",
"MailTo": "to@example.com",
"MailFrom": "from@example.com",
"MailSubject": "Hello",
"Body": "Test email",
"Priority": "High"
}
public class EmailService
{
private readonly HttpClient _httpClient;
private readonly ILogger<EmailService> _logger;
private string _jwtToken;
private readonly string _authUrl = "https://your-func-url/api/gettoken";
private readonly string _functionUrl = "https://your-func-url/api/SendEmail";
public EmailService(HttpClient httpClient, ILogger<EmailService> logger)
{
_httpClient = httpClient;
_logger = logger;
}
public async Task InitializeAsync(string appName, string apiKey)
{
var authRequest = new { AppName = appName, ApiKey = apiKey };
var response = await _httpClient.PostAsJsonAsync(_authUrl, authRequest);
response.EnsureSuccessStatusCode();
var authResponse = await response.Content.ReadFromJsonAsync<AuthResponse>();
_jwtToken = authResponse.Token;
}
public async Task SendEmailAsync(EmailRequest emailRequest)
{
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _jwtToken);
var response = await _httpClient.PostAsJsonAsync(_functionUrl, emailRequest);
response.EnsureSuccessStatusCode();
}
}
[ApiController]
[Route("api/[controller]")]
public class EmailController : ControllerBase
{
private readonly EmailService _emailService;
public EmailController(EmailService emailService)
{
_emailService = emailService;
}
[HttpPost]
public async Task<IActionResult> SendEmail()
{
await _emailService.InitializeAsync("ClientApp1", "your-api-key");
await _emailService.SendEmailAsync(new EmailRequest
{
AppName = "ClientApp1",
MailTo = "to@example.com",
MailFrom = "from@example.com",
MailSubject = "Sample Subject",
Body = "Test Body",
Priority = "High"
});
return Ok("Email sent.");
}
}