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