Development

Chuyển SMTP sang Microsoft Graph API: Gửi Email .NET Bảo Mật

By Ginbok5 min read

Tại sao cần Chuyển đổi từ SMTP?

Microsoft đang ngừng sử dụng Xác thực Cơ bản (Basic Authentication) cho Exchange Online, khiến SMTP trở nên lỗi thời đối với các ứng dụng hiện đại.

Tại sao chọn Graph API?


Điều kiện tiên quyết


So sánh Trước và Sau

❌ Cũ: SMTP với Mật khẩu

<smtp deliveryMethod="Network" from="notifications@company.com">
      <network host="smtp.office365.com" port="587" 
               userName="notifications@company.com" 
               password="SuperSecretPassword123!" />
    </smtp>
    

Vấn đề: Mật khẩu dạng văn bản thuần, không có xác thực hiện đại, gọi đồng bộ (synchronous), khó kiểm thử

✅ Mới: Graph API với OAuth2

<appSettings>
      <add key="Email:ClientId" value="your-client-id" />
      <add key="Email:ClientSecret" value="your-secret" />
      <add key="Email:TenantId" value="your-tenant-id" />
      <add key="Email:SenderMailbox" value="notifications@company.com" />
    </appSettings>
    

Lợi ích: Thông tin xác thực OAuth2, hoạt động bất đồng bộ (async), dễ kiểm thử, tính lâu dài


Thiết lập Azure AD (Các bước nhanh)

  1. Đăng ký Ứng dụng: Azure Portal → App registrations → New registration
  2. Thêm Quyền: API permissions → Microsoft Graph → Application → Mail.Send
  3. Cấp Quyền đồng ý: Nhấp vào "Grant admin consent"
  4. Tạo Secret: Certificates & secrets → New client secret → Sao chép giá trị
  5. Lưu các Giá trị: Client ID, Tenant ID, Secret, Sender UPN

Triển khai: Các thành phần cốt lõi

1. Nhà máy Xác thực (Authentication Factory)

using Azure.Identity;
using Microsoft.Graph;

public static class GraphClientFactory
{
    public static GraphServiceClient Create(EmailConfig config)
    {
        var credential = new ClientSecretCredential(
            config.TenantId,
            config.ClientId,
            config.ClientSecret,
            new ClientSecretCredentialOptions 
            { 
                AuthorityHost = AzureAuthorityHosts.AzurePublicCloud 
            }
        );

        return new GraphServiceClient(credential, 
            new[] { "https://graph.microsoft.com/.default" });
    }
}
    

2. Mô hình Cấu hình (Configuration Model)

public class EmailConfig
{
    public string ClientId { get; set; }
    public string ClientSecret { get; set; }
    public string TenantId { get; set; }
    public string SenderMailbox { get; set; }
    
    public static EmailConfig LoadFromConfig() => new()
    {
        ClientId = ConfigurationManager.AppSettings["Email:ClientId"],
        ClientSecret = ConfigurationManager.AppSettings["Email:ClientSecret"],
        TenantId = ConfigurationManager.AppSettings["Email:TenantId"],
        SenderMailbox = ConfigurationManager.AppSettings["Email:SenderMailbox"]
    };
}
    

3. Trình xây dựng Email Fluent (Fluent Email Builder)

using Microsoft.Graph.Models;
using Microsoft.Graph.Users.Item.SendMail;

public class EmailBuilder
{
    private string _subject, _from, _bodyHtml;
    private List<string> _recipients = new();

    public static EmailBuilder Create() => new();

    public EmailBuilder SetSubject(string subject) 
    { 
        _subject = subject; 
        return this; 
    }

    public EmailBuilder SetFrom(string from) 
    { 
        _from = from; 
        return this; 
    }

    public EmailBuilder SetBodyHtml(string html) 
    { 
        _bodyHtml = html; 
        return this; 
    }

    public EmailBuilder AddRecipients(params string[] emails) 
    { 
        _recipients.AddRange(emails); 
        return this; 
    }

    public SendMailPostRequestBody Build() => new()
    {
        Message = new Message
        {
            Subject = _subject,
            Body = new ItemBody 
            { 
                ContentType = BodyType.Html, 
                Content = _bodyHtml 
            },
            From = new Recipient 
            { 
                EmailAddress = new EmailAddress { Address = _from } 
            },
            ToRecipients = _recipients.Select(e => new Recipient 
            { 
                EmailAddress = new EmailAddress { Address = e } 
            }).ToList()
        },
        SaveToSentItems = false
    };
}

4. Dịch vụ Email (Email Service)

public interface IEmailService
{
    Task SendEmailAsync(SendMailPostRequestBody body, EmailConfig config);
}

public class GraphEmailService : IEmailService
{
    public async Task SendEmailAsync(SendMailPostRequestBody body, EmailConfig config)
    {
        var client = GraphClientFactory.Create(config);
        await client.Users[config.SenderMailbox].SendMail.PostAsync(body);
    }
}

5. Ví dụ Sử dụng (Usage Example)

var config = EmailConfig.LoadFromConfig();

var email = EmailBuilder.Create()
    .SetSubject("Welcome!")
    .SetFrom(config.SenderMailbox)
    .SetBodyHtml("<h1>Hello</h1><p>Welcome to our platform</p>")
    .AddRecipients("user@example.com")
    .Build();

var service = new GraphEmailService();
await service.SendEmailAsync(email, config);

Lỗi Thường gặp & Giải pháp

1. "Authorization_RequestDenied"

Nguyên nhân: Thiếu quyền API (API permissions)
Khắc phục: Azure Portal → API permissions → Thêm Mail.Send → Grant admin consent → Đợi 5 phút

2. "MailboxNotFound"

Nguyên nhân: UPN không hợp lệ
Khắc phục: Xác minh email người gửi tồn tại trong tenant Microsoft 365 và là hộp thư (không phải danh sách phân phối)

3. "Client secret has expired"

Nguyên nhân: Secret đã hết hạn (tối đa 24 tháng)
Khắc phục: Tạo secret mới trong Azure Portal → Cập nhật cấu hình → Khởi động lại ứng dụng

4. "InvalidAuthenticationToken"

Nguyên nhân: Lỗi khi lấy token xác thực
Khắc phục: Xác minh ClientId, TenantId, Secret là GUID chính xác và không có khoảng trắng thừa

Kết luận

Chuyển đổi từ SMTP sang Microsoft Graph API giúp hiện đại hóa cơ sở hạ tầng email của bạn với bảo mật và tính năng tốt hơn.

Những gì Chúng ta Đã Đạt được:
✅ Xác thực OAuth2 (không mật khẩu)
✅ Dịch vụ email có thể tái sử dụng và kiểm thử được
✅ API Fluent để soạn email
✅ Khả năng tương thích ngược an toàn
✅ Triển khai sẵn sàng cho sản xuất

Bài học Chính:

Ứng dụng của bạn giờ đây đã có tính lâu dài và sẵn sàng cho việc tích hợp Microsoft 365 hiện đại.

#GraphAPI DotNet Backend Security
← Back to Articles
Chuyển SMTP sang Microsoft Graph API: Gửi Email .NET Bảo Mật - Ginbok