pexels-kevin-ku-577585

Engineering

MS Office 365 Exchange OAuth2 IMAP Authentication with Flowable - Part 2 Access Mailbox

DECEMBER 2, 2022

In the last couple of months, Microsoft has been gradually deprecating and disabling basic authentication for Microsoft Exchange Online protocols like POP and IMAP. The last opportunity to switch to the new, modern, OAuth 2.0-based authentication is by end of December 2022.

Welcome to Part 2 of this series. Part 1 covered the Azure setup and testing. Now we are going to showcase how to configure Flowable and Spring Boot and finally how to use plain Spring and javax.mail to access a mailbox over IMAP. Missed Part 1? Read it here.

OAuth2 Client Registration Config

Flowable products use the Spring Security OAuth2 Client library to configure OAuth2 authentication to 3rd-party servers.

We are starting with the Spring Security OAuth2 Client configuration. The following listing shows properties to obtain access tokens for Microsoft Office 365 Exchange Online, using the client credentials flow:

spring.security.oauth2.client.provider.exchange-server-imap.issuer-uri=https://login.microsoftonline.com/<tenantId>/v2.0
spring.security.oauth2.client.registration.exchange-server-imap.client-id=<application_client_id>
spring.security.oauth2.client.registration.exchange-server-imap.client-secret=<client_secret>
spring.security.oauth2.client.registration.exchange-server-imap.scope=https://outlook.office365.com/.default
spring.security.oauth2.client.registration.exchange-server-imap.authorization-grant-type=client_credentials

The exchange-server-imap is the client registration id. It is a custom name and can be named as desired. Multiple providers and registrations are possible to authenticate against multiple OAuth2 endpoints. You can find the Endpoints of the issuer-uri, alongside all the other available Endpoints for a registered app in Microsoft Azure here:

New Expression util: flwAuthTokenUtils

With the upcoming Flowable Enterprise v3.13 and v3.12.5 it is possible to obtain access tokens wherever expressions are supported. The expression expects a serviceId, which maps to the client registration id above:

${flwAuthTokenUtils.getAccessToken('exchange-server-imap')}

Flowables EMail inbound channel with Office 365 OAuth2

The following example showcases this new expression util. It is used to configure an EMail Inbound channel and uses this new functionality in the password field of the IMAP configuration. Please also note the required custom imaps.* properties are required to successfully connect to Microsoft Office 365 Exchange Online with this authentication method:

NOTE: The expression used in the "password" field is a SpEl Expression, therefore it starts with #{ and not with ${. Adjust the interval and the other properties to your needs. Those are just used as an example here. You might want to read your messages as read, etc.

The default implementation takes care of caching the token and only requesting a new one, when the token is expired. Also, token expiration is being taken care of (see the chapter below for details).

IDLE for Office 365 Exchange Online

IDLE is the alternative for polling mode in IMAP. Instead of polling for messages, IDLE mode pushes to the application in case a new Email arrives in the mailbox. This sounds good at the first glance; however, it turns out to be more error-prone in practice, with different problems from vendor to vendor. Additionally, not all SMTP providers support IDLE. Office 365 supports IDLE in general, however, the community reported some problems in the past regarding the stability of this feature: MS Techcommunity article regarding imap idle.

Long story short: Polling with a reasonable interval seems to be the more stable solution for now. In any case: Start with polling, get the functionality done and try out if IDLE works for your use case, if you would like to rely on it.

Troubleshoot? Enable debug properties

Add mail.debug debug properties to the "Custom properties" section, to get details printed to the console:

For a full example of how easy it is to receive and handle EMails with Flowable and how to model some process or case logic around it, see the how-to guide on receiving and handling EMails with Flowable from Valentin Zickner. Also check our helpful how-to video guides on YouTube for tons of different topics on what Flowable is capable of.

Filip Hrisafov used the EMail inbound channel in his Flowfest'22 session "Event-based sharding for hyper-scaling", which is available on YouTube. Watch it, as long as it still is hot.

javax.mail and Spring APIs

This section discusses an example of how to use Spring Security OAuth2 Client API and javax.mail API to connect to an Exchange Online mailbox. This example is independent of Flowable APIs. We are happy to share to our knowledge in this regard. Remember? Our open source thinking.

NOTE: Access tokens do expire. Expiration needs to be handled in the application code. This approach shown below, using an Authenticator, takes care of that by taking advantage of the javax.mail connection pool: It removes the not-authenticated connection (when the token expires) from the pool and creates a new authenticated one.

BEWARE: You might need to adapt your existing code creating the javax.mail.Session, in case the password is passed as static string. It will work first in tests, but FAIL with authentication errors when the token expires!

How to do it right?

First, let's create a @Service capable of returning an access token for a username and clientRegistrationId:

@Service
class OAuth2TokenService {
  AuthorizedClientServiceOAuth2AuthorizedClientManager clients;

  OAuth2TokenService(ClientRegistrationRepository repo, OAuth2AuthorizedClientService service) {
  this.clients = new AuthorizedClientServiceOAuth2AuthorizedClientManager(repo, service);
  }

  String fetchAccessToken(String username, String clientRegistrationId) {
      OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
                 .withClientRegistrationId(clientRegistrationId)
                .principal(username)
                .build();
      OAuth2AuthorizedClient authorizedClient = clients.authorize(authorizeRequest);
       return authorizedClient.getAccessToken().getTokenValue();
    }
}

Next, we use this service in a special TokenAuthenticator extending Authenticator to fetch and use the access token as password:

class TokenAuthenticator extends javax.mail.Authenticator {
   String username; String serviceId; OAuth2TokenService tokenService;

   // Constructor omitted

   @Override
    protected PasswordAuthentication getPasswordAuthentication() {
        String accessToken = tokenService.fetchAccessToken(username, serviceId);
        return new PasswordAuthentication(username, accessToken);
    }
}

Finally the TokenAuthenticator is used, when creating the javax.mail Session:

String email = "mymail@acme.com";
Properties props = new Properties();
props.put("mail.store.protocol", "imaps");
props.put("mail.imaps.host", "outlook.office365.com");
props.put("mail.imaps.port", "993");
props.put("mail.imaps.ssl.enable", "true");
props.put("mail.imaps.starttls.enable", "true");
props.put("mail.imaps.auth", "true");
props.put("mail.imaps.auth.mechanisms", "XOAUTH2");
props.put("mail.imaps.user", email);
props.put("mail.imaps.auth.plain.disable", "true"); 
props.put("mail.imaps.auth.xoauth2.disable", "false"); 
// Dependecy-Injected OAuth2TokenService 
Authenticator authenticator = new TokenAuthenticator(email, "exchange-server-imap", this.oauth2TokenService); 
// Important to use authenticator here, for token based auth 
Session session = Session.getInstance(props, authenticator); 
session.setDebug(true); Store store = session.getStore("imaps"); 
store.connect(); 
Folder folder = store.getFolder("Inbox"); 
folder.open(Folder.READ_ONLY);
System.out.printf("Nr of messages in mailbox: %d", folder.getMessageCount());

Note the usage of the Authenticator, instead of hard-coding username / token.

Conclusion

Part 2 of this limited series on using modern OAuth2-based authentication to access Microsoft Office 365 Exchange Online mailboxes showcased

  • How to configure Flowables Mail inbound channel using the new expression util method ${flwAuthTokenUtils.getAccessToken('oauth-server')} to fetch access tokens and how to use it in the mail inbound channel configuration instead of a literal passsword.

  • How to correctly use javax.mail APIs to using the authentication mechanism to not run into authentication errors due to expired tokens.

Part 1 covered the Azure setup.

I hope you found some valuable insights in this series. Feel free to share the articles if you found them useful.

Arthur Hupka-Merle

Senior Software Architect

Flowable Product Software Architect and passionate software enthusiast. Has 15 years of experience writing and using enterprise software in the business rules and decision management domain. Co-founder and organizer of a heavy metal festival in Germany.

Share this Blog post
pexels-google-deepmind-18069697
Engineering | FEBRUARY 19, 2024
The Value of AI in Modeling

As AI gains prominence as a pivotal technology and enterprises increasingly seek to leverage its capabilities, we are actively exploring diverse avenues for integrating AI into process automation.

pixabay_egg-583163_1920_stevepb
Engineering | OCTOBER 3, 2023
Low-code, High Impact: Flowable & CMMN for Complex Use Cases

The key to managing complexity is to combine different and multiple tools leads to better, faster, and more maintainable solutions. For example, combining BPMN with CMMN.

AdobeStock_566576699
Engineering | OCTOBER 2, 2023
The New Flowable eLearning Platform

Discover the reasons behind our brand-new Flowable eLearning platform and explore its features by registering for our inaugural free course.