Posts Tagged ‘OAuth 2.0’

Office365: Temporary server error. Please try again later. PRX4

Thursday, January 28th, 2021

When using SMTP with Office365 and OAUTH 2.0 you may receive this error:

Temporary server error. Please try again later. PRX4

SMTP logs look like this:

Connecting to ‘outlook.office365.com:587’, SSL/TLS: False.

S: 220 AS8PR04CA0136.outlook.office365.com Microsoft ESMTP MAIL Service ready at Thu, 28 Jan 2021 15:43:35 + 0000
C: EHLO[IPv6:2a02:]
S: 250-AS8PR04CA0136.outlook.office365.com Hello[2a02:]
S: 250-SIZE 157286400
S: 250-PIPELINING
S: 250-DSN
S: 250-ENHANCEDSTATUSCODES
S: 250-STARTTLS
S: 250-8BITMIME
S: 250-BINARYMIME
S: 250-CHUNKING
S: 250 SMTPUTF8
C: STARTTLS
S: 220 2.0.0 SMTP server ready
C: EHLO[IPv6:2a02:]
S: 250-AS8PR04CA0136.outlook.office365.com Hello [2a02:]
S: 250-SIZE 157286400
S: 250-PIPELINING
S: 250-DSN
S: 250-ENHANCEDSTATUSCODES
S: 250-AUTH LOGIN XOAUTH2
S: 250-8BITMIME
S: 250-BINARYMIME
S: 250-CHUNKING
S: 250 SMTPUTF8
C: AUTH XOAUTH2 dXNlcj1B...EEBAQ==
S: 451 4.7.0 Temporary server error. Please try again later. PRX4[AS8PR04CA0136.eurprd04.prod.outlook.com]

 It is a bug when setting up an office365 business account with Microsoft. 

After creating the e-mail account, you have to edit the account (go to https://admin.microsoft.com/)

Then go to “Mail” tab then click “Manage email apps

There is an option called “Authenticated SMTP”.  It is ticked by default, however to actually make it work you have to uncheck it and save the changes, then go back in and re-check it and save the changes.

You may need to leave it unchecked for some time and wait for some time when it is rechecked before it starts working:

C: AUTH XOAUTH2 dXNlcj1B...BAQ==
S: 235 2.7.0 Authentication successful
C: QUIT

OAuth 2.0 password grant with Office365/Exchange IMAP/POP3/SMTP

Thursday, November 19th, 2020

First you need to register your application in Azure Portal.

Here’s a detailed guide how to do that:
https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app

Enable additional flows:

Then you need to apply correct API permissions and grant the admin consent for your domain.

This image has an empty alt attribute; its file name is image-1.png

In the API permissions / Add a permission wizard, select Microsoft Graph and then Delegated permissions to find the following permission scopes listed:

  • offline_access
  • email
  • IMAP.AccessAsUser.All
  • POP.AccessAsUser.All
  • SMTP.Send

Remember to grant admin consent.

Use Microsoft Authentication Library for .NET (MSAL.NET) nuget package to obtain an access token:
https://www.nuget.org/packages/Microsoft.Identity.Client/

string clientId = "Application (client) ID";
string tenantId = "Directory (tenant) ID";

string userEmail = "Username for mailbox";
string userPassword = "Password for that user";

IPublicClientApplication app = PublicClientApplicationBuilder.Create(clientId)
    .WithAuthority(AzureCloudInstance.AzurePublic, tenantId)
    .Build();

var scopes = new string[] 
{
    "offline_access",
    "email",
    "https://outlook.office.com/IMAP.AccessAsUser.All",
    "https://outlook.office.com/POP.AccessAsUser.All",
    "https://outlook.office.com/SMTP.Send",
};

Now acquire the access token and user email address:

var accounts = await app.GetAccountsAsync();

AuthenticationResult result;
if (accounts.Any())
{
    IAccount account = accounts.FirstOrDefault();
    result = await app.AcquireTokenSilent(scopes, account).ExecuteAsync();
}
else
{
    SecureString securePassword = new SecureString();
    foreach (char c in userPassword)
    {
        securePassword.AppendChar(c);
    }
    result = await app.AcquireTokenByUsernamePassword(
        scopes, 
        userEmail, 
        securePassword).ExecuteAsync();
}

string user = result.Account.Username;
string accessToken = result.AccessToken;

Finally you can connect to IMAP/POP3/SMTP server and authenticate:

using (Imap client = new Imap())
{
    client.ConnectSSL("outlook.office365.com");
    client.LoginOAUTH2(user, accessToken);
 
    client.SelectInbox();

    // ...

    client.Close();
} 

OAuth 2.0 with Office365/Exchange IMAP/POP3/SMTP

Tuesday, June 23rd, 2020

First you need to register your application in Azure Portal.

Here’s a detailed guide how to do that:
https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app

Remember to add authentication entries (localhost is needed for .net core):

Then you need to apply correct API permissions and grant the admin consent for your domain.

In the API permissions / Add a permission wizard, select Microsoft Graph and then Delegated permissions to find the following permission scopes listed:

  • offline_access
  • email
  • IMAP.AccessAsUser.All
  • POP.AccessAsUser.All
  • SMTP.Send

Remember to grant admin consent.

Use Microsoft Authentication Library for .NET (MSAL.NET) nuget package to obtain an access token:

https://www.nuget.org/packages/Microsoft.Identity.Client/

var pcaOptions = new PublicClientApplicationOptions
{
    ClientId = "Application (client) ID",

    TenantId = "Directory (tenant) ID",
    // -or-
    // for @outlook.com/@hotmail accounts instead of setting TenantId use:
    // AadAuthorityAudience = AadAuthorityAudience.PersonalMicrosoftAccount, 

    RedirectUri = "https://login.microsoftonline.com/common/oauth2/nativeclient"
    // RedirectUri = "http://localhost" // for .net core
};

var pca = PublicClientApplicationBuilder
    .CreateWithApplicationOptions(pcaOptions)
    .Build();

var scopes = new string[] 
{
    "offline_access",
    "email",
    "https://outlook.office.com/IMAP.AccessAsUser.All",
    "https://outlook.office.com/POP.AccessAsUser.All",
    "https://outlook.office.com/SMTP.Send",
};

In addition, you can request for offline_access scope. When a user approves the offline_access scope, your app can receive refresh tokens from the Microsoft identity platform token endpoint. Refresh tokens are long-lived. Your app can get new access tokens as older ones expire.

Now acquire the access token and user email address:

var authResult = pca.AcquireTokenInteractive(scopes).ExecuteAsync().Result;

string user = authResult.Account.Username;
string accessToken = authResult.AccessToken;

Finally you can connect to IMAP/POP3/SMTP server and authenticate:

using (Imap client = new Imap())
{
    client.ConnectSSL("outlook.office365.com");
    client.LoginOAUTH2(user, accessToken);
 
    client.SelectInbox();

    // ...

    client.Close();
} 

As this is fairly new feature for Exchange/Office365, here are some useful links:

https://docs.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth

https://stackoverflow.com/questions/29747477/imap-auth-in-office-365-using-oauth2

https://stackoverflow.com/questions/43473858/connect-to-outlook-office-365-imap-using-oauth2

https://stackoverflow.com/questions/61597263/office-365-xoauth2-for-imap-and-smtp-authentication-fails

OAuth 2.0 with Gmail over IMAP for installed applications

Monday, March 13th, 2017

OAuth is an open protocol to allow secure API authorization in a simple and standard method from desktop and web applications.

This article describes using OAuth 2.0 to access Gmail IMAP and SMTP servers using .NET IMAP component in installed applications scenario. You can also use OAuth 2.0 for web applications.

Google.Apis

Use Nuget to download “Google.Apis.Auth” package.

Import namespaces:

// c#

using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Requests;
using Google.Apis.Auth.OAuth2.Responses;

using Limilabs.Client.Authentication.Google;

using Limilabs.Client.IMAP;
' VB.NET 

Imports Google.Apis.Auth.OAuth2
Imports Google.Apis.Auth.OAuth2.Flows
Imports Google.Apis.Auth.OAuth2.Requests
Imports Google.Apis.Auth.OAuth2.Responses

Imports Limilabs.Client.Authentication.Google

Imports Limilabs.Client.IMAP

Register Application

Before you can use OAuth 2.0, you must register your application using the Google Developers Console.

At least product name must be specified:

Now create credentials:

After you’ve registered, copy the “Client ID” and “Client secret” values, which you’ll need later:

Now we can define clientID, clientSecret and scope variables, as well as Google OAuth 2.0 server addresses. Scope basically specifies what services we want to have access to. In our case it is user’s email address and IMAP/SMTP access:

// c#

string clientID = "XXX.apps.googleusercontent.com";
string clientSecret = "IxBs0g5sdaSDUz4Ea7Ix-Ua";

var clientSecrets = new ClientSecrets
{
    ClientId = clientID,
    ClientSecret = clientSecret
};

var credential = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
    ClientSecrets = clientSecrets,
    Scopes = new[] { GoogleScope.ImapAndSmtp.Name, GoogleScope.UserInfoEmailScope.Name}
});
' VB.NET 

Dim clientID As String = "XXX.apps.googleusercontent.com"
Dim clientSecret As String = "IxBs0g5sdaSDUz4Ea7Ix-Ua"

Dim clientSecrets = New ClientSecrets With { _
	.ClientId = clientID, _
	.ClientSecret = clientSecret _
}

Dim credential = New GoogleAuthorizationCodeFlow(New GoogleAuthorizationCodeFlow.Initializer With { _
	.ClientSecrets = clientSecrets, _
	.Scopes = {GoogleScope.ImapAndSmtp.Name, GoogleScope.UserInfoEmailScope.Name} _
})

Obtain an OAuth 2.0 access token

Now we’ll create authorization url:


AuthorizationCodeRequestUrl url = credential.CreateAuthorizationCodeRequest("urn:ietf:wg:oauth:2.0:oob");

Process.Start(url.Build().ToString());
' VB.NET 

Dim url As AuthorizationCodeRequestUrl = credential.CreateAuthorizationCodeRequest("urn:ietf:wg:oauth:2.0:oob")

Process.Start(url.Build().ToString())

We are using Process.Start here, but you can also embed WebBrowser control in your application.

At this point user is redirected to Google to authorize the access:

After this step user is presented a code that needs to be pasted to your application:

Please note that this code also appears in the title of the browser:

  • It is possible to monitor processes on your machine and act automatically when it is there.
  • If you use embedded WebBrowser control in your application, you can monitor the HTML document title after any redirect.

Following is a code that reads this code and contacts Google to exchange it for a refresh-token and an access-token:

string authCode = Console.ReadLine();

TokenResponse token = await credential.ExchangeCodeForTokenAsync("", authCode, "urn:ietf:wg:oauth:2.0:oob", CancellationToken.None);

string accessToken = token.AccessToken;
' VB.NET 

Dim authCode As String = Console.ReadLine()

Dim token As TokenResponse = Await credential.ExchangeCodeForTokenAsync("", authCode, "urn:ietf:wg:oauth:2.0:oob", CancellationToken.None)

Dim accessToken As String = token.AccessToken

An access token is usually valid for a maximum of one hour, and allows you to access the user’s data. You also received a refresh token. A refresh token can be used to request a new access token once the previous expired.

Access IMAP/SMTP server

Finally we’ll ask Google for user’s email and use LoginOAUTH2 method to access Gmail’s IMAP server:

// c#

GoogleApi api = new GoogleApi(accessToken);
string user = api.GetEmail();

using (Imap imap = new Imap())
{
    imap.ConnectSSL("imap.gmail.com");
    imap.LoginOAUTH2(user, accessToken);

    imap.SelectInbox();
    List<long> uids = imap.Search(Flag.Unseen);

    foreach (long uid in uids)
    {
        var eml = imap.GetMessageByUID(uid);
        IMail email = new MailBuilder().CreateFromEml(eml);
        Console.WriteLine(email.Subject);
    }
    imap.Close();
}
' VB.NET 

Dim api As New GoogleApi(accessToken)
Dim user As String = api.GetEmail()

Using imap As New Imap()
	imap.ConnectSSL("imap.gmail.com")
	imap.LoginOAUTH2(user, accessToken)

	imap.SelectInbox()
	Dim uids As List(Of Long) = imap.Search(Flag.Unseen)

	For Each uid As Long In uids
		Dim eml = imap.GetMessageByUID(uid)
		Dim email As IMail = New MailBuilder().CreateFromEml(eml)
		Console.WriteLine(email.Subject)
	Next
	imap.Close()
End Using

Refreshing access token

An access token is usually short lived and valid for a maximum of one hour. The main reason behind this is security and prevention of replay attacks. This means that for long-lived applications you need to refresh the access token.

Your refresh token will be sent only once – don’t loose it!

We recommend storing entire TokenResponse object received from GoogleAuthorizationCodeFlow.ExchangeCodeForTokenAsync method call. This object contains both: refresh token and access token, along with its expiration time.

The process of refreshing access token is simple:

// c#

TokenResponse refreshed = await credential.RefreshTokenAsync("", token.RefreshToken, CancellationToken.None);

' VB.NET 

Dim refreshed As TokenResponse = Await credential.RefreshTokenAsync("", token.RefreshToken, CancellationToken.None)

OAuth 2.0 with Gmail over IMAP for web applications

Monday, March 13th, 2017

OAuth is an open protocol to allow secure API authorization in a simple and standard method from desktop and web applications.

This article describes using OAuth 2.0 to access Gmail IMAP and SMTP servers using .NET IMAP component in web application scenario (ASP.NET/ASP.NET MVC). You can also use OAuth 2.0 for installed applications.

Google.Apis

Use Nuget to download “Google.Apis.Auth” package.

Import namespaces:

// c#

using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Requests;
using Google.Apis.Auth.OAuth2.Responses;

using Limilabs.Client.Authentication.Google;

using Limilabs.Client.IMAP;
' VB.NET 

Imports Google.Apis.Auth.OAuth2
Imports Google.Apis.Auth.OAuth2.Flows
Imports Google.Apis.Auth.OAuth2.Requests
Imports Google.Apis.Auth.OAuth2.Responses

Imports Limilabs.Client.Authentication.Google

Imports Limilabs.Client.IMAP

Register Application

Before you can use OAuth 2.0, you must register your application using the Google Developers Console. After you’ve registered, go to the API Access tab and copy the “Client ID” and “Client secret” values and specify “Redirect URI“, which you’ll need later.

At least product name must be specified:

Now create credentials:

Specify redirect URI:

After you’ve registered, copy the “Client ID” and “Client secret” values, which you’ll need later:

Now we can define clientID, clientSecret, redirect url and scope variables, as well as Google OAuth 2.0 server addresses. Scope basically specifies what services we want to have access to. In our case it is user’s email address and IMAP/SMTP access:

// c#

string clientID = "XXX.apps.googleusercontent.com";
string clientSecret = "IxBs0g5sdaSDUz4Ea7Ix-Ua";
string redirectUri = "http://www.yourdomain.com/oauth2callback";

var clientSecrets = new ClientSecrets
{
    ClientId = clientID,
    ClientSecret = clientSecret
};

var credential = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
    ClientSecrets = clientSecrets,
    Scopes = new[] { GoogleScope.ImapAndSmtp.Name, GoogleScope.UserInfoEmailScope.Name}
});
' VB.NET 

Dim clientID As String = "XXX.apps.googleusercontent.com"
Dim clientSecret As String = "IxBs0g5sdaSDUz4Ea7Ix-Ua"
Dim redirectUri As String = "http://www.yourdomain.com/oauth2callback"

Dim clientSecrets = New ClientSecrets With { _
	.ClientId = clientID, _
	.ClientSecret = clientSecret _
}

Dim credential = New GoogleAuthorizationCodeFlow(New GoogleAuthorizationCodeFlow.Initializer With { _
	.ClientSecrets = clientSecrets, _
	.Scopes = {GoogleScope.ImapAndSmtp.Name, GoogleScope.UserInfoEmailScope.Name} _
})

Obtain an OAuth 2.0 access token

Now we’ll create authorization url:


AuthorizationCodeRequestUrl url = credential.CreateAuthorizationCodeRequest(redirectUri);

' VB.NET 

Dim url As AuthorizationCodeRequestUrl = credential.CreateAuthorizationCodeRequest(redirectUri)

Now we need to redirect the client:

// c#

return new RedirectResult(url.Build().ToString());

' VB.NET 

Return New RedirectResult(url.Build().ToString())

At this point user is redirected to Google to authorize the access:

After this step user is redirected back to your website (http://www.yourdomain.com/oauth2callback), with code request parameter:
http://www.yourdomain.com/oauth2callback?code=4/5Y7M4cARD9hrt0nuKnQa0YgtYMasdbwprRtIIjk4Fus#

// c#

public class OAauth2CallbackController : Controller
{
    public ActionResult Index(string code)
    {
        ...
    }
}
' VB.NET 

Public Class OAauth2CallbackController
    Inherits Controller
    Public Function Index(code As String) As ActionResult
        ...
    End Function
End Class

Following is this callback code. Its purpose is to get a refresh-token and an access-token:

// c#

string authCode = code;

TokenResponse token = await credential.ExchangeCodeForTokenAsync("", authCode, redirectUri, CancellationToken.None);

string accessToken = token.AccessToken;
' VB.NET 

Dim authCode As String = code

Dim token As TokenResponse = Await credential.ExchangeCodeForTokenAsync("", authCode, redirectUri, CancellationToken.None)

Dim accessToken As String = token.AccessToken

An access token is usually valid for a maximum of one hour, and allows you to access the user’s data. You also received a refresh token. A refresh token can be used to request a new access token once the previous expired.

Access IMAP/SMTP server

Finally we’ll ask Google for user’s email and use LoginOAUTH2 method to access Gmail’s IMAP server:

// c#

GoogleApi api = new GoogleApi(accessToken);
string user = api.GetEmail();

using (Imap imap = new Imap())
{
    imap.ConnectSSL("imap.gmail.com");
    imap.LoginOAUTH2(user, accessToken);

    imap.SelectInbox();
    List<long> uids = imap.Search(Flag.Unseen);

    foreach (long uid in uids)
    {
        var eml = imap.GetMessageByUID(uid);
        IMail email = new MailBuilder().CreateFromEml(eml);
        Console.WriteLine(email.Subject);
    }
    imap.Close();
}
' VB.NET 

Dim api As New GoogleApi(accessToken)
Dim user As String = api.GetEmail()

Using imap As New Imap()
	imap.ConnectSSL("imap.gmail.com")
	imap.LoginOAUTH2(user, accessToken)

	imap.SelectInbox()
	Dim uids As List(Of Long) = imap.Search(Flag.Unseen)

	For Each uid As Long In uids
		Dim eml = imap.GetMessageByUID(uid)
		Dim email As IMail = New MailBuilder().CreateFromEml(eml)
		Console.WriteLine(email.Subject)
	Next
	imap.Close()
End Using

Refreshing access token

An access token is usually short lived and valid for a maximum of one hour. The main reason behind this is security and prevention of replay attacks. This means that for long-lived applications you need to refresh the access token.

Your refresh token will be sent only once – don’t loose it!

We recommend storing entire TokenResponse object received from GoogleAuthorizationCodeFlow.ExchangeCodeForTokenAsync method call. This object contains both: refresh token and access token, along with its expiration time.

The process of refreshing access token is simple:

// c#

TokenResponse refreshed = await credential.RefreshTokenAsync("", token.RefreshToken, CancellationToken.None);

' VB.NET 

Dim refreshed As TokenResponse = Await credential.RefreshTokenAsync("", token.RefreshToken, CancellationToken.None)