دسترسی به سرویس‌های ایمیل با استفاده از OAuth

Aspose.Email برای .NET از OAuth 2.0 پشتیبانی می‌کند که می‌تواند برای دسترسی به سرورهای SMTP، POP3، IMAP و EWS استفاده شود. به طور کلی، هر سروری که توکن‌های حامل OAuth 2.0 را پشتیبانی می‌کند می‌تواند با Aspose.Email استفاده شود، اما کلاینت‌های ایمیل با سرورهای ایمیل گوگل و سرورهای Microsoft Office 365 تست شده‌اند.

دسترسی به سرور از SmtpClient, Pop3Client, ImapClient، و EWSClient با OAuth می‌تواند به دو شکل پیاده‌سازی شود:

  1. توکن دسترسی را مستقیماً به سازنده یک کلاینت ایمیل فراهم کنید. در این حالت باید در نظر داشته باشید که زمان عمر توکن‌های دسترسی محدود است. وقتی توکن منقضی شود، دیگر نمی‌توان از کلاینت ایمیل برای دسترسی به سرور استفاده کرد.
  2. ارائه یک پیاده‌سازی سفارشی از ارائه‌کننده توکن بر پایه ITokenProvider رابط به سازنده یک کلاینت ایمیل. در این حالت، کلاینت زمان انقضای توکن را بررسی می‌کند و توکن دسترسی جدیدی را از ITokenProvider وقتی توکن قبلی منقضی شده است. این روش باعث می‌شود کلاینت توکن‌ها را به‌صورت دوره‌ای تازه‌سازی کند و بتواند برای مدت نامحدود با سرور کار کند. اکثر سرویس‌ها یک روش ساده برای تازه‌سازی توکن‌های دسترسی ارائه می‌دهند. برای مثال، توکن‌های تازه‌سازی در سرویس‌های گوگل، یا جریان احراز هویت ROPC در پلتفرم هویت مایکروسافت، می‌توانند برای پیاده‌سازی یک ارائه‌کننده توکن استفاده شوند.

پیکربندی حساب بر روی سرور مناسب

مقالات زیر به شما در پیکربندی حساب‌ها برای دسترسی به خدمات ایمیل کمک می‌کنند:

دسترسی به خدمات ایمیل با توکن‌های دسترسی

مثال‌های کد زیر نشان می‌دهند که چگونه با استفاده از توکن‌های دسترسی به خدمات ایمیل متصل شوید.

// Connecting to SMTP server
using (SmtpClient client = new SmtpClient(
    "smtp.gmail.com",
    587,
    "user1@gmail.com",
    "accessToken",
    true,
    SecurityOptions.SSLExplicit))
{

}

// Connecting to IMAP server
using (ImapClient client = new ImapClient(
   "imap.gmail.com",
   993,
   "user1@gmail.com",
   "accessToken",
   true,
   SecurityOptions.SSLImplicit))
{

}

// Connecting to POP3 server
using (Pop3Client client = new Pop3Client(
   "pop.gmail.com",
   995,
   "user1@gmail.com",
   "accessToken",
   true,
   SecurityOptions.Auto))
{

}

دسترسی به خدمات ایمیل با ارائه‌کنندگان توکن

نمونه کدهای زیر نشان می‌دهند چگونه به سرویس‌های ایمیل با استفاده از یک فراهم‌کنندهٔ توکن متصل شوید. توکن توکار TokenProvider.Google کارخانه برای دریافت یک فراهم‌کننده استفاده می‌شود که توکن‌های دسترسی را به‌صورت خودکار با استفاده از توکن تجدید Google تازه می‌کند.

ITokenProvider tokenProvider = TokenProvider.Google.GetInstance(
    "ClientId",
    "ClientSecret",
    "RefreshToken");

// Connecting to SMTP server
using (SmtpClient client = new SmtpClient(
    "smtp.gmail.com",
    587,
    "user1@gmail.com",
    tokenProvider,
    SecurityOptions.SSLExplicit))
{

}

// Connecting to IMAP server
using (ImapClient client = new ImapClient(
   "imap.gmail.com",
   993,
   "user1@gmail.com",
   tokenProvider,
   SecurityOptions.SSLImplicit))
{

}

// Connecting to POP3 server
using (Pop3Client client = new Pop3Client(
   "pop.gmail.com",
   995,
   "user1@gmail.com",
   tokenProvider,
   SecurityOptions.Auto))
{

}

پیاده‌سازی یک ITokenProvider سفارشی برای Office 365

وقتی یک فراهم‌کنندهٔ توکن توکار برای سناریوی شما در دسترس نیست، می‌توانید ITokenProvider خودتان این رابط را پیاده کنید. مثال زیر جریان Azure Resource Owner Password Credential (ROPC) را برای دریافت و تجدید توکن‌های دسترسی سرویس‌های ایمیل Office 365 پیاده‌سازی می‌کند. GetAccessToken متد توکن را کش می‌کند و فقط وقتی توکن جدیدی از سرور درخواست می‌شود که توکن فعلی منقضی شده باشد.

using JsonConvert = Newtonsoft.Json.JsonConvert;
using Aspose.Email.Clients;
using Aspose.Email.Common.Utils;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Net;
using System.Text;

namespace TestNS
{
    /// <summary>
    /// Azure resource owner password credential (ROPC) token provider
    /// https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth-ropc
    /// https://portal.azure.com
    /// https://developer.microsoft.com/en-us/graph/graph-explorer/#
    /// token parser https://jwt.io
    /// </summary>
    internal class AzureROPCTokenProvider : ITokenProvider
    {
        private const string uriFormat = "https://login.microsoftonline.com/{0}/oauth2/v2.0/token";
        private const string bodyFormat =
            "client_id={0}" +
            "&scope={1}" +
            "&username={2}" +
            "&password={3}" +
            "&grant_type={4}";

        private readonly string scope;
        private const string grant_type = "password";
        private readonly object tokenSyncObj = new object();
        private OAuthToken token;
        private readonly string tenant;
        private readonly string clientId;
        private readonly string clientSecret;
        private readonly string userName;
        private readonly string password;

        /// <summary>
        /// Initializes a new instance of the <see cref="AzureROPCTokenProvider"/> class
        /// </summary>
        public AzureROPCTokenProvider(
            string tenant,
            string clientId,
            string clientSecret,
            string userName,
            string password,
            string[] scopeAr)
        {
            this.tenant = tenant;
            this.clientId = clientId;
            this.clientSecret = clientSecret;
            this.userName = userName;
            this.password = password;
            this.scope = string.Join(" ", scopeAr);
        }

        /// <summary>
        /// Gets oAuth access token.
        /// </summary>
        /// <param name="ignoreExistingToken">
        /// If ignoreExistingToken is true, requests a new token from the server. Otherwise the behaviour depends on whether the token exists or not.
        /// If the token exists and its expiration date is not expired, returns the current token, otherwise requests a new token from the server.
        /// </param>
        /// <returns>Returns oAuth access token</returns>
        public virtual OAuthToken GetAccessToken(bool ignoreExistingToken)
        {
            lock (tokenSyncObj)
            {
                if (this.token != null && !this.token.Expired && !ignoreExistingToken)
                    return this.token;
                token = null;
                string uri = string.Format(uriFormat, string.IsNullOrWhiteSpace(tenant) ? "common" : tenant);
                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
                string body = string.Format(bodyFormat,
                    HttpUtility.UrlEncode(clientId),
                    HttpUtility.UrlEncode(scope),
                    HttpUtility.UrlEncode(userName),
                    HttpUtility.UrlEncode(password),
                    HttpUtility.UrlEncode(grant_type));
                byte[] bytes = Encoding.ASCII.GetBytes(body);
                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";
                request.ContentLength = bytes.Length;
                using (Stream requestStream = request.GetRequestStream())
                    requestStream.Write(bytes, 0, bytes.Length);
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                StringBuilder responseText = new StringBuilder();
                bytes = new byte[1024];
                int read = 0;
                using (Stream stream = response.GetResponseStream())
                {
                    while ((read = stream.Read(bytes, 0, bytes.Length)) > 0)
                        responseText.Append(Encoding.ASCII.GetString(bytes, 0, read));
                }
                string jsonString = responseText.ToString();
                AzureTokenResponse t = JsonConvert.DeserializeObject<AzureTokenResponse>(jsonString);
                token = new OAuthToken(
                    t.access_token,
                    TokenType.AccessToken,
                    DateTime.Now.AddSeconds(t.expires_in));
                return token;
            }
        }

        /// <summary>
        /// Gets oAuth access token.
        /// If the token exists and its expiration date is not expired, returns the current token, otherwise requests a new token from the server.
        /// </summary>
        /// <returns>Returns oAuth access token</returns>
        public OAuthToken GetAccessToken()
        {
            return GetAccessToken(false);
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public virtual void Dispose()
        {
        }
    }

    /// <summary>
    /// A success response contains a JSON OAuth 2.0 response with the following parameters.
    /// </summary>
    public class AzureTokenResponse
    {
        /// <summary>
        /// The requested access token. The calling web service can use this token to authenticate to the receiving web service.
        /// </summary>
        public string access_token { get; set; }

        /// <summary>
        /// Indicates the token type value. The only type that Azure AD supports is Bearer. For more information about bearer tokens,
        /// see The OAuth 2.0 Authorization Framework: Bearer Token Usage (RFC 6750).
        /// </summary>
        public string token_type { get; set; }

        /// <summary>
        /// How long the access token is valid (in seconds).
        /// </summary>
        public int expires_in { get; set; }

        /// <summary>
        /// How long the access token is valid (in seconds).
        /// </summary>
        public int ext_expires_in { get; set; }

        /// <summary>
        /// The time when the access token expires.
        /// The date is represented as the number of seconds from 1970-01-01T00:00:00Z UTC until the expiration time.
        /// This value is used to determine the lifetime of cached tokens.
        /// </summary>
        public int expires_on { get; set; }

        /// <summary>
        /// The App ID URI of the receiving web service.
        /// </summary>
        public string resource { get; set; }

        /// <summary>
        /// If an access token was returned, this parameter lists the scopes the access token is valid for.
        /// </summary>
        public string scope { get; set; }

        /// <summary>
        /// Issued if the original scope parameter included the openid scope.
        /// </summary>
        public string id_token { get; set; }

        /// <summary>
        /// Issued if the original scope parameter included offline_access.
        /// </summary>
        public string refresh_token { get; set; }
    }
}

مثال کد بعدی نشان می‌دهد چگونه به سرویس‌های Office 365 با استفاده از فراهم‌کنندهٔ توکن سفارشی پیاده‌سازی‌شده در بالا متصل شوید. همان نمونهٔ فراهم‌کننده می‌تواند به کلاینت‌های SMTP، IMAP، POP3 و EWS پاس داده شود.

ITokenProvider tokenProvider = new AzureROPCTokenProvider(
    "Tenant",
    "ClientId",
    "ClientSecret",
    "EMail",
    "Password",
    scopes);

// Connecting to SMTP server
using (SmtpClient client = new SmtpClient(
    "smtp.office365.com",
    587,
    "Test1@test.onmicrosoft.com",
    tokenProvider,
    SecurityOptions.SSLExplicit))
{

}

// Connecting to IMAP server
using (ImapClient client = new ImapClient(
    "outlook.office365.com",
    993,
    "Test1@test.onmicrosoft.com",
    tokenProvider,
    SecurityOptions.SSLImplicit))
{

}

// Connecting to POP3 server
using (Pop3Client client = new Pop3Client(
   "outlook.office365.com",
   995,
   "Test1@test.onmicrosoft.com",
   tokenProvider,
   SecurityOptions.Auto))
{

}

// Connecting to EWS server
const string mailboxUri = "https://outlook.office365.com/ews/exchange.asmx";
ICredentials credentials = new OAuthNetworkCredential(tokenProvider);
using (IEWSClient ewsClient = EWSClient.GetEWSClient(mailboxUri, credentials))
{

}