Authenticate a connection using OAuth 2.0

The following example shows how to use GemBox.Email to authenticate against a remote IMAP server using the OAuth 2.0 mechanism.

using GemBox.Email;
using GemBox.Email.Imap;
using System;

class Program
{
    static void Main()
    {
        // If using the Professional version, put your serial key below.
        ComponentInfo.SetLicense("FREE-LIMITED-KEY");

        // Create a new IMAP client.
        using (var imap = new ImapClient("<ADDRESS> (e.g. imap.gmail.com)"))
        {
            //  Connect and sign to IMAP server using OAuth 2.0.
            imap.Connect();
            imap.Authenticate("<USERNAME>", "<ACCESS-TOKEN>", ImapAuthentication.XOAuth2);
            Console.WriteLine("Authenticated.");
        }
    }
}
Imports GemBox.Email
Imports GemBox.Email.Imap
Imports System

Module Program

    Sub Main()

        ' If using the Professional version, put your serial key below.
        ComponentInfo.SetLicense("FREE-LIMITED-KEY")

        ' Create a new IMAP client.
        Using imap As New ImapClient("<ADDRESS> (e.g. imap.gmail.com)")

            '  Connect and sign to IMAP server using OAuth 2.0.
            imap.Connect()
            imap.Authenticate("<USERNAME>", "<ACCESS-TOKEN>", ImapAuthentication.XOAuth2)
            Console.WriteLine("Authenticated.")
        End Using
    End Sub
End Module

The OAuth 2.0 authorization framework is a protocol that enables a third-party application to obtain limited access to the user's protected resources without using the resource owner's credentials. Instead of using the resource owner's credentials to access protected resources, the client obtains an access token - a string denoting a specific scope, lifetime, and other access attributes.

You can follow the next example if you need to authenticate with OAuth 2.0 when connecting to a POP server.

using GemBox.Email;
using GemBox.Email.Pop;
using System;

class Program
{
    static void Main()
    {
        // If using the Professional version, put your serial key below.
        ComponentInfo.SetLicense("FREE-LIMITED-KEY");

        // Create a new POP client.
        using (var pop = new PopClient("<ADDRESS> (e.g. pop.gmail.com)"))
        {
            //  Connect and sign to POP server using OAuth 2.0.
            pop.Connect();
            pop.Authenticate("<USERNAME>", "<ACCESS-TOKEN>", PopAuthentication.XOAuth2);
            Console.WriteLine("Authenticated.");
        }
    }
}
Imports GemBox.Email
Imports GemBox.Email.Pop
Imports System

Module Program

    Sub Main()

        ' If using the Professional version, put your serial key below.
        ComponentInfo.SetLicense("FREE-LIMITED-KEY")

        ' Create a new POP client.
        Using pop As New PopClient("<ADDRESS> (e.g. pop.gmail.com)")

            '  Connect and sign to POP server using OAuth 2.0.
            pop.Connect()
            pop.Authenticate("<USERNAME>", "<ACCESS-TOKEN>", PopAuthentication.XOAuth2)
            Console.WriteLine("Authenticated.")
        End Using
    End Sub
End Module

The same thing applies when connecting to SmtpClient with the OAuth 2.0 method, as you can see in the following example.

using GemBox.Email;
using GemBox.Email.Smtp;
using System;

class Program
{
    static void Main()
    {
        // If using the Professional version, put your serial key below.
        ComponentInfo.SetLicense("FREE-LIMITED-KEY");

        // Create a new SMTP client.
        using (var smtp = new SmtpClient("<ADDRESS> (e.g. smtp.gmail.com)"))
        {
            //  Connect and sign to SMTP server using OAuth 2.0.
            smtp.Connect();
            smtp.Authenticate("<USERNAME>", "<ACCESS-TOKEN>", SmtpAuthentication.XOAuth2);
            Console.WriteLine("Authenticated.");
        }
    }
}
Imports GemBox.Email
Imports GemBox.Email.Smtp
Imports System

Module Program

    Sub Main()

        ' If using the Professional version, put your serial key below.
        ComponentInfo.SetLicense("FREE-LIMITED-KEY")

        ' Create a new SMTP client.
        Using smtp As New SmtpClient("<ADDRESS> (e.g. smtp.gmail.com)")

            '  Connect and sign to SMTP server using OAuth 2.0.
            smtp.Connect()
            smtp.Authenticate("<USERNAME>", "<ACCESS-TOKEN>", SmtpAuthentication.XOAuth2)
            Console.WriteLine("Authenticated.")
        End Using
    End Sub
End Module

If you need to use the ExchangeClient, use the following example as a reference.

using GemBox.Email;
using GemBox.Email.Exchange;
using System;

class Program
{
    static void Main()
    {
        // If using the Professional version, put your serial key below.
        ComponentInfo.SetLicense("FREE-LIMITED-KEY");

        // Create a new Exchange client.
        var exchangeClient = new ExchangeClient("<HOST> (e.g. https://outlook.office365.com/EWS/Exchange.asmx)");
        // Authenticate the client using OAuth 2.0.
        exchangeClient.Authenticate("<USERNAME>", "<ACCESS-TOKEN>", ExchangeAuthentication.OAuth2);
    }
}
Imports GemBox.Email
Imports GemBox.Email.Exchange
Imports System

Module Program

    Sub Main()

        ' If using the Professional version, put your serial key below.
        ComponentInfo.SetLicense("FREE-LIMITED-KEY")

        ' Create a new Exchange client.
        Dim exchangeClient = New ExchangeClient("<HOST> (e.g. https://outlook.office365.com/EWS/Exchange.asmx)")
        ' Authenticate the client using OAuth 2.0.
        exchangeClient.Authenticate("<USERNAME>", "<ACCESS-TOKEN>", ExchangeAuthentication.OAuth2)
    End Sub
End Module

Finally, if you need to use the GraphClient, use the following example as a reference.

using GemBox.Email;
using GemBox.Email.Graph;
using System;

class Program
{
    static void Main()
    {
        // If using the Professional version, put your serial key below.
        ComponentInfo.SetLicense("FREE-LIMITED-KEY");

        // Create a new Graph client.
        var graphClient = new GraphClient();
        // Authenticate the client using OAuth 2.0.
        graphClient.Authenticate("<ACCESS-TOKEN>");
    }
}
Imports GemBox.Email
Imports GemBox.Email.Graph
Imports System

Module Program

    Sub Main()

        ' If using the Professional version, put your serial key below.
        ComponentInfo.SetLicense("FREE-LIMITED-KEY")

        ' Create a new Graph client.
        Dim graphClient = New GraphClient()
        ' Authenticate the client using OAuth 2.0.
        graphClient.Authenticate("<ACCESS-TOKEN>")
    End Sub
End Module

Using OAuth 2.0 to Access Gmail

The Gmail IMAP, POP, and SMTP servers have been extended to support the OAuth 2.0 protocol for authentication and authorization.

All applications follow a basic pattern when accessing Gmail accounts using OAuth 2.0. At a high level, you follow these steps:

1. Obtain OAuth 2.0 credentials from the Google API Console.

Visit the Google API Console to obtain OAuth 2.0 credentials, such as a client ID and client secret known to both Google and your application.

2. Obtain an access token from the Google Authorization Server.

Obtain an access token from the Google Authorization Server. Before your application can access protected resources from Gmail servers, it must obtain an access token that grants access to those protected resources. A single access token can grant varying degrees of access to multiple protected resources. A variable parameter called scope controls the set of resources and operations that an access token permits. During the access token request, your application sends one or more values in the scope parameter.

There are several ways to make this request, which vary based on the type of application you are building. For example, a JavaScript application might request an access token using a browser redirect to Google. In contrast, an application installed on a device without a browser uses web service requests.

Some requests require an authentication step where the user logs in with their Google account. After logging in, the user can choose whether they are willing to grant one or more permissions that your application is requesting. This process is called user consent.

Suppose the user grants at least one permission. In that case, the Google Authorization Server sends your application an access token (or an authorization code that your application can use to obtain an access token) and a list of scopes of access granted by that token. If the user does not give permission, the server returns an error.

3. Use the access token to authenticate to a user's Gmail account.

After an application obtains an access token, it uses it to authenticate to a user's Gmail account.

The code below shows how to do an OAuth 2.0 authorization flow from a Windows Console application.

using GemBox.Email;
using GemBox.Email.Smtp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;

class Program
{
    static void Main()
    {
        // If using the Professional version, put your serial key below.
        ComponentInfo.SetLicense("FREE-LIMITED-KEY");

        const string clientID = "<CLIENT-ID>";
        const string clientSecret = "<CLIENT-SECRET>";

        // Generates code verifier value.
        string codeVerifier = RandomDataBase64Url(32);

        // Creates a redirect URI using an available port on the loopback address.
        string redirectURI = string.Format("http://{0}:{1}/", IPAddress.Loopback, GetRandomUnusedPort());
        Console.WriteLine("redirect URI: " + redirectURI);

        // Extracts the authorization code.
        var authorizationCode = GetAuthorizationCode(clientID, codeVerifier, redirectURI);

        // Obtains the access token from the authorization code.
        string accessToken = GetAccessToken(authorizationCode, clientID, clientSecret, codeVerifier, redirectURI);

        // Uses the access token to authenticate to a user's Gmail account.
        using (var smtp = new SmtpClient("<ADDRESS> (e.g. smtp.gmail.com)"))
        {
            smtp.Connect();
            smtp.Authenticate("<USERNAME>", accessToken, SmtpAuthentication.XOAuth2);
            Console.WriteLine("Authenticated.");
        }
    }

    static string GetAuthorizationCode(string clientID, string codeVerifier, string redirectURI)
    {
        // Generates state and PKCE values.
        string state = RandomDataBase64Url(32);
        string codeChallenge = Base64UrlEncodeNoPadding(Sha256(codeVerifier));
        const string codeChallengeMethod = "S256";

        // Creates an HttpListener to listen for requests on that redirect URI.
        var http = new HttpListener();
        http.Prefixes.Add(redirectURI);
        Console.WriteLine("Listening..");
        http.Start();

        // Creates the OAuth 2.0 authorization request.
        string authorizationRequestURI = "https://accounts.google.com/o/oauth2/v2/auth";
        string scope = "https://mail.google.com/";

        string authorizationRequest = string.Format("{0}?response_type=code&scope={6}&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}",
            authorizationRequestURI,
            Uri.EscapeDataString(redirectURI),
            clientID,
            state,
            codeChallenge,
            codeChallengeMethod,
            Uri.EscapeDataString(scope));

        // Opens request in the browser.
        System.Diagnostics.Process.Start(authorizationRequest);

        // Waits for the OAuth authorization response.
        var context = http.GetContext();

        // Sends an HTTP response to the browser.
        var response = context.Response;
        string responseString = string.Format("<html><head><meta http-equiv='refresh' content='10;url=https://google.com'></head><body>Please return to the app.</body></html>");
        var buffer = Encoding.UTF8.GetBytes(responseString);
        response.ContentLength64 = buffer.Length;
        using (var responseOutput = response.OutputStream)
            responseOutput.Write(buffer, 0, buffer.Length);
        http.Stop();
        Console.WriteLine("HTTP server stopped.");

        // Checks for errors.
        if (context.Request.QueryString.Get("error") != null)
        {
            Console.WriteLine(String.Format("OAuth authorization error: {0}.", context.Request.QueryString.Get("error")));
            return null;
        }

        if (context.Request.QueryString.Get("code") == null || context.Request.QueryString.Get("state") == null)
        {
            Console.WriteLine("Malformed authorization response. " + context.Request.QueryString);
            return null;
        }

        // Extracts the code.
        var code = context.Request.QueryString.Get("code");
        var incomingState = context.Request.QueryString.Get("state");

        // Compares the receieved state to the expected value, to ensure that this app made the request which resulted in authorization.
        if (incomingState != state)
        {
            Console.WriteLine(String.Format("Received request with invalid state ({0})", incomingState));
            return null;
        }

        Console.WriteLine("Authorization code: " + code);
        return code;
    }

    static string GetAccessToken(string code, string clientID, string clientSecret, string codeVerifier, string redirectURI)
    {
        Console.WriteLine("Exchanging code for tokens...");

        // Builds the request.
        string tokenRequestURI = "https://www.googleapis.com/oauth2/v4/token";
        string tokenRequestBody = string.Format("code={0}&redirect_uri={1}&client_id={2}&code_verifier={3}&client_secret={4}&grant_type=authorization_code",
            code,
            Uri.EscapeDataString(redirectURI),
            clientID,
            codeVerifier,
            clientSecret);

        // Sends the reques.
        HttpWebRequest tokenRequest = (HttpWebRequest)WebRequest.Create(tokenRequestURI);
        tokenRequest.Method = "POST";
        tokenRequest.ContentType = "application/x-www-form-urlencoded";
        tokenRequest.Accept = "Accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
        byte[] tokenRequestBytes = Encoding.ASCII.GetBytes(tokenRequestBody);
        tokenRequest.ContentLength = tokenRequestBytes.Length;
        Stream stream = tokenRequest.GetRequestStream();
        stream.Write(tokenRequestBytes, 0, tokenRequestBytes.Length);
        stream.Close();

        try
        {
            // Gets the response.
            WebResponse tokenResponse = tokenRequest.GetResponse();
            using StreamReader reader = new StreamReader(tokenResponse.GetResponseStream());
            // Reads response body.
            string responseText = reader.ReadToEnd();
            Console.WriteLine(responseText);

            // Converts to dictionary.
            Dictionary<string, string> tokenEndpointDecoded = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, string>>(responseText);

            string accessToken = tokenEndpointDecoded["access_token"];
            return accessToken;
        }
        catch (WebException ex)
        {
            if (ex.Status == WebExceptionStatus.ProtocolError)
            {
                if (ex.Response is HttpWebResponse response)
                {
                    Console.WriteLine("HTTP: " + response.StatusCode);
                    using StreamReader reader = new StreamReader(response.GetResponseStream());
                    // Reads response body.
                    string responseText = reader.ReadToEnd();
                    Console.WriteLine(responseText);
                }
            }
            return null;
        }
    }

    private static int GetRandomUnusedPort()
    {
        var listener = new TcpListener(IPAddress.Loopback, 0);
        listener.Start();
        var port = ((IPEndPoint)listener.LocalEndpoint).Port;
        listener.Stop();
        return port;
    }

    private static string RandomDataBase64Url(uint length)
    {
        RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
        byte[] bytes = new byte[length];
        rng.GetBytes(bytes);
        return Base64UrlEncodeNoPadding(bytes);
    }

    private static byte[] Sha256(string inputStirng)
    {
        byte[] bytes = Encoding.ASCII.GetBytes(inputStirng);
        SHA256Managed sha256 = new SHA256Managed();
        return sha256.ComputeHash(bytes);
    }

    private static string Base64UrlEncodeNoPadding(byte[] buffer)
    {
        string base64 = Convert.ToBase64String(buffer);
        // Converts base64 to base64url.
        base64 = base64.Replace("+", "-");
        base64 = base64.Replace("/", "_");
        // Strips padding.
        base64 = base64.Replace("=", "");
        return base64;
    }
}
Imports GemBox.Email
Imports GemBox.Email.Smtp
Imports System
Imports System.Collections.Generic
Imports System.Diagnostics
Imports System.IO
Imports System.Net
Imports System.Net.Sockets
Imports System.Security.Cryptography
Imports System.Text

Module Program

    Sub Main()
        ' If using the Professional version, put your serial key below.
        ComponentInfo.SetLicense("FREE-LIMITED-KEY")

        Const clientID As String = "<CLIENT-ID>"
        Const clientSecret As String = "<CLIENT-SECRET>"

        ' Generates code verifier value.
        Dim codeVerifier As String = RandomDataBase64Url(32)

        ' Creates a redirect URI using an available port on the loopback address.
        Dim redirectURI As String = String.Format("http://{0}:{1}/", IPAddress.Loopback, GetRandomUnusedPort())
        Console.WriteLine("redirect URI: " & redirectURI)

        ' Extracts the authorization code.
        Dim authorizationCode = GetAuthorizationCode(clientID, codeVerifier, redirectURI)

        ' Obtains the access token from the authorization code.
        Dim accessToken As String = GetAccessToken(authorizationCode, clientID, clientSecret, codeVerifier, redirectURI)

        ' Uses the access token to authenticate to a user's Gmail account.
        Using smtp As New SmtpClient("<ADDRESS> (e.g. smtp.gmail.com)")
            smtp.Connect()
            smtp.Authenticate("<USERNAME>", accessToken, SmtpAuthentication.XOAuth2)
            Console.WriteLine("Authenticated.")
        End Using
    End Sub

    Function GetAuthorizationCode(clientID As String, codeVerifier As String, redirectURI As String) As String
        ' Generates state and PKCE values.
        Dim state As String = RandomDataBase64Url(32)
        Dim codeChallenge As String = Base64UrlEncodeNoPadding(Sha256(codeVerifier))
        Const codeChallengeMethod As String = "S256"

        ' Creates an HttpListener to listen for requests on that redirect URI.
        Dim http As New HttpListener()
        http.Prefixes.Add(redirectURI)
        Console.WriteLine("Listening...")
        http.Start()

        ' Creates the OAuth 2.0 authorization request.
        Dim authorizationRequestURI As String = "https://accounts.google.com/o/oauth2/v2/auth"
        Dim scope As String = "https://mail.google.com/"

        Dim authorizationRequest As String = String.Format("{0}?response_type=code&scope={6}&redirect_uri={1}&client_id={2}&state={3}&code_challenge={4}&code_challenge_method={5}",
            authorizationRequestURI,
            Uri.EscapeDataString(redirectURI),
            clientID,
            state,
            codeChallenge,
            codeChallengeMethod,
            Uri.EscapeDataString(scope))

        ' Opens request in the browser.
        Process.Start(authorizationRequest)

        ' Waits for the OAuth authorization response.
        Dim context = http.GetContext()

        ' Sends an HTTP response to the browser.
        Dim response = context.Response
        Dim responseString As String = String.Format("<html><head><meta http-equiv='refresh' content='10;url=https://google.com'></head><body>Please return to the app.</body></html>")
        Dim buffer = Encoding.UTF8.GetBytes(responseString)
        response.ContentLength64 = buffer.Length
        Using responseOutput = response.OutputStream
            responseOutput.Write(buffer, 0, buffer.Length)
        End Using
        http.Stop()
        Console.WriteLine("HTTP server stopped.")

        ' Checks for errors.
        If context.Request.QueryString.Get("error") IsNot Nothing Then
            Console.WriteLine(String.Format("OAuth authorization error: {0}.", context.Request.QueryString.Get("error")))
            Return Nothing
        End If

        If context.Request.QueryString.Get("code") Is Nothing OrElse context.Request.QueryString.Get("state") Is Nothing Then
            Console.WriteLine("Malformed authorization response. " & context.Request.QueryString.ToString())
            Return Nothing
        End If

        ' Extracts the code.
        Dim code = context.Request.QueryString.Get("code")
        Dim incomingState = context.Request.QueryString.Get("state")

        ' Compares the receieved state to the expected value, to ensure that this app made the request which resulted in authorization.
        If incomingState <> state Then
            Console.WriteLine(String.Format("Received request with invalid state ({0})", incomingState))
            Return Nothing
        End If

        Console.WriteLine("Authorization code: " & code)
        Return code
    End Function

    Function GetAccessToken(code As String, clientID As String, clientSecret As String, codeVerifier As String, redirectURI As String) As String
        Console.WriteLine("Exchanging code for tokens...")

        ' Builds the request.
        Dim tokenRequestURI As String = "https://www.googleapis.com/oauth2/v4/token"
        Dim tokenRequestBody As String = String.Format("code={0}&redirect_uri={1}&client_id={2}&code_verifier={3}&client_secret={4}&grant_type=authorization_code",
            code,
            Uri.EscapeDataString(redirectURI),
            clientID,
            codeVerifier,
            clientSecret)

        ' Sends the request.
        Dim tokenRequest As HttpWebRequest = CType(WebRequest.Create(tokenRequestURI), HttpWebRequest)
        tokenRequest.Method = "POST"
        tokenRequest.ContentType = "application/x-www-form-urlencoded"
        tokenRequest.Accept = "Accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
        Dim tokenRequestBytes As Byte() = Encoding.ASCII.GetBytes(tokenRequestBody)
        tokenRequest.ContentLength = tokenRequestBytes.Length
        Dim stream As Stream = tokenRequest.GetRequestStream()
        stream.Write(tokenRequestBytes, 0, tokenRequestBytes.Length)
        stream.Close()

        Try
            ' Gets the response.
            Dim tokenResponse As WebResponse = tokenRequest.GetResponse()
            Using reader As New StreamReader(tokenResponse.GetResponseStream())
                ' Reads response body.
                Dim responseText As String = reader.ReadToEnd()
                Console.WriteLine(responseText)

                ' Converts to dictionary.
                Dim tokenEndpointDecoded As Dictionary(Of String, String) = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Dictionary(Of String, String))(responseText)

                Dim accessToken As String = tokenEndpointDecoded("access_token")
                Return accessToken
            End Using
        Catch ex As WebException

            If ex.Status = WebExceptionStatus.ProtocolError Then

                Dim response As HttpWebResponse = TryCast(ex.Response, HttpWebResponse)
                If response IsNot Nothing Then
                    Console.WriteLine("HTTP: " & response.StatusCode)
                    Using reader As StreamReader = New StreamReader(response.GetResponseStream())
                        ' Reads response body.
                        Dim responseText As String = reader.ReadToEnd()
                        Console.WriteLine(responseText)
                    End Using
                End If
            End If

            Return Nothing
        End Try
    End Function

    Function GetRandomUnusedPort() As Integer
        Dim listener As New TcpListener(IPAddress.Loopback, 0)
        listener.Start()
        Dim port = CType(listener.LocalEndpoint, IPEndPoint).Port
        listener.Stop()
        Return port
    End Function

    Function RandomDataBase64Url(length As UInteger) As String
        Dim rng As New RNGCryptoServiceProvider()
        Dim bytes As Byte() = New Byte(length - 1) {}
        rng.GetBytes(bytes)
        Return Base64UrlEncodeNoPadding(bytes)
    End Function

    Function Sha256(inputStirng As String) As Byte()
        Dim bytes As Byte() = Encoding.ASCII.GetBytes(inputStirng)
        Dim sha256Manager As New SHA256Managed()
        Return sha256Manager.ComputeHash(bytes)
    End Function

    Function Base64UrlEncodeNoPadding(ByVal buffer As Byte()) As String
        Dim base64 As String = Convert.ToBase64String(buffer)
        ' Converts base64 to base64url.
        base64 = base64.Replace("+", "-")
        base64 = base64.Replace("/", "_")
        ' Strips padding.
        base64 = base64.Replace("=", "")
        Return base64
    End Function

End Module

See also


Next steps

GemBox.Email is a .NET component that enables you to read, write, receive, and send emails from your .NET applications using one simple API.

Download Buy