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
CREATETABLEAllowedApps (
Id INTPRIMARY KEY IDENTITY,
AppName NVARCHAR(100) NOT NULL,
ApiKey NVARCHAR(200) NOT NULL,
IsActive BITNOT NULL
);
CREATETABLEEmailLog (
Id INTPRIMARY 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"
}
publicclassEmailService{privatereadonlyHttpClient_httpClient;privatereadonlyILogger<EmailService>_logger;privatestring_jwtToken;privatereadonlystring_authUrl="https://your-func-url/api/gettoken";privatereadonlystring_functionUrl="https://your-func-url/api/SendEmail";publicEmailService(HttpClienthttpClient,ILogger<EmailService>logger){_httpClient=httpClient;_logger=logger;}publicasyncTaskInitializeAsync(stringappName,stringapiKey){varauthRequest=new{AppName=appName,ApiKey=apiKey};varresponse=await_httpClient.PostAsJsonAsync(_authUrl,authRequest);response.EnsureSuccessStatusCode();varauthResponse=awaitresponse.Content.ReadFromJsonAsync<AuthResponse>();_jwtToken=authResponse.Token;}publicasyncTaskSendEmailAsync(EmailRequestemailRequest){_httpClient.DefaultRequestHeaders.Authorization=newAuthenticationHeaderValue("Bearer",_jwtToken);varresponse=await_httpClient.PostAsJsonAsync(_functionUrl,emailRequest);response.EnsureSuccessStatusCode();}}[ApiController][Route("api/[controller]")]publicclassEmailController:ControllerBase{privatereadonlyEmailService_emailService;publicEmailController(EmailServiceemailService){_emailService=emailService;}[HttpPost]publicasyncTask<IActionResult>SendEmail(){await_emailService.InitializeAsync("ClientApp1","your-api-key");await_emailService.SendEmailAsync(newEmailRequest{AppName="ClientApp1",MailTo="to@example.com",MailFrom="from@example.com",MailSubject="Sample Subject",Body="Test Body",Priority="High"});returnOk("Email sent.");}}