Zugriff auf Mail‑Dienste mittels OAuth

Aspose.Email für .NET unterstützt OAuth 2.0, das zum Zugriff auf SMTP, POP3, IMAP und EWS Server verwendet werden kann. Im Allgemeinen kann jeder Server, der OAuth‑2.0‑Bearer‑Token unterstützt, mit Aspose.Email genutzt werden, aber die E‑Mail‑Clients wurden mit Google‑Mail‑Servern und Microsoft Office 365‑Servern getestet.

Zugriff auf einen Server von SmtpClient, Pop3Client, ImapClient, und EWSClient mit OAuth kann auf zwei Arten implementiert werden:

  1. Ein Zugriffstoken direkt bereitstellen für den Konstruktor eines E‑Mail‑Clients. In diesem Fall muss berücksichtigt werden, dass die Lebensdauer von Zugriffstoken begrenzt ist. Wenn das Token abläuft, kann der E‑Mail‑Client nicht mehr zum Zugriff auf den Server verwendet werden.
  2. Eine benutzerdefinierte Implementierung eines Token‑Anbieters bereitstellen basierend auf dem ITokenProvider Schnittstelle zum Konstruktor eines E‑Mail‑Clients. In diesem Fall prüft der Client das Ablaufdatum des Tokens und fordert ein neues Zugriffstoken vom ITokenProvider wenn das vorherige abgelaufen ist. Auf diese Weise aktualisiert der Client periodisch Token und kann unbegrenzt mit dem Server arbeiten. Die meisten Dienste unterstützen eine einfache Methode zum Aktualisieren von Zugriffstoken. Beispielsweise können Refresh‑Token in Google‑Diensten oder der ROPC‑Authentifizierungsfluss in der Microsoft‑Identitätsplattform verwendet werden, um einen Token‑Anbieter zu implementieren.

Ein Konto auf dem passenden Server konfigurieren

Die folgenden Artikel helfen Ihnen, Konten zu konfigurieren, um auf Maildienste zuzugreifen:

Zugriff auf Maildienste mit Zugriffstoken

Die folgenden Codebeispiele zeigen, wie man mit Zugriffstoken eine Verbindung zu Maildiensten herstellt.

// 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))
{

}

Zugriff auf Maildienste mit Token-Anbietern

Die folgenden Code‑Beispiele zeigen, wie man sich mit Mail‑Diensten über einen Token‑Provider verbindet. Der integrierte TokenProvider.Google Fabrik wird verwendet, um einen Provider zu erhalten, der Zugriffstoken automatisch mit einem Google‑Refresh‑Token erneuert.

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))
{

}

Implementieren Sie einen benutzerdefinierten ITokenProvider für Office 365

Wenn kein integrierter Token‑Provider für Ihr Szenario verfügbar ist, können Sie den ITokenProvider Implementieren Sie das Interface selbst. Das nachfolgende Beispiel implementiert den Azure Resource Owner Password Credential (ROPC)‑Flow, um Zugriffs‑Tokens für Office 365‑Mail‑Dienste zu erhalten und zu erneuern. Der GetAccessToken Methode cached das Token und fordert nur ein neues vom Server an, wenn das aktuelle Token abgelaufen ist.

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; }
    }
}

Das nächste Code‑Beispiel zeigt, wie man sich mit Office 365‑Diensten über den oben implementierten benutzerdefinierten Token‑Provider verbindet. dieselbe Provider‑Instanz kann an die SMTP‑, IMAP‑, POP3‑ und EWS‑Clients übergeben werden.

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))
{

}