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