Skip to content

Issue 53534: Support Microsoft's Graph Protocol API for authentication via OAuth2 token#7381

Open
labkey-bpatel wants to merge 32 commits intodevelopfrom
fb_mail_transport_via_graph
Open

Issue 53534: Support Microsoft's Graph Protocol API for authentication via OAuth2 token#7381
labkey-bpatel wants to merge 32 commits intodevelopfrom
fb_mail_transport_via_graph

Conversation

@labkey-bpatel
Copy link
Contributor

@labkey-bpatel labkey-bpatel commented Feb 3, 2026

Rationale

Microsoft plans to retire SMTP Basic Authentication in March 2026. In response, one of our clients’ IT department has requested a shift to OAuth-based authentication

Since SMTP Oauth 2.0 client credential flow with non-interactive sign-in (for email notifications) is not supported we’ll need to use Microsoft’s Graph API instead of SMTP protocol

This PR adds a Graph transport method as an alternative to SMTP, which enables mail delivery via OAuth2 authentication method.

Spec

Related Pull Requests

Changes

  • Add EmailTransportProvider interface.
  • Extract SMTP logic from MailHelper to its own provider class.
  • Add GraphTransportProvider that implements Graph API email transport using Microsoft SDK, handles attachments >3MB via upload sessions to accommodate Graph API's 4MB request limit.
  • Update MailHelper to manage multiple transport providers and detect configuration conflict (if both SMTP and Graph are configured)
  • Add EmailTestWithAttachmentAction in AdminController to test Graph API with HTML content, large attachments and data URI images.
  • Update emailTest.jsp to display success feedback after email send attempts and add Graph-specific test button for HTML and attachment workflow (button only visible when Graph is the active provider).
  • Add unit test to mock Microsoft Graph SDK to verify API interactions without real credentials.

Tasks 📍

  • Manual Testing
  • Unit test
  • Verify Fix

@github-actions
Copy link

github-actions bot commented Feb 3, 2026

WARNING: This PR appears to have the default title generated by GitHub. Please use something more descriptive.

@labkey-bpatel labkey-bpatel changed the title Fb mail transport via graph Issue 53534: Support Microsoft's Graph Protocol API for authentication via OAuth2 token Feb 3, 2026
@labkey-bpatel labkey-bpatel marked this pull request as ready for review February 5, 2026 00:46
Copy link
Contributor

@labkey-adam labkey-adam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good, generally speaking. No required changes, but please review my questions & comments.

My biggest concern is the fact that this adds 70MB of JAR files to API. This is an extraordinary amount of new code for a single feature that will be used rarely. Were there any other options that would reduce the dependencies? Can any dependencies be excluded? If we can't slim it down, we may need to consider pushing the Graph API transport and dependencies into a separate module.

@labkey-bpatel
Copy link
Contributor Author

labkey-bpatel commented Feb 24, 2026

Code looks good, generally speaking. No required changes, but please review my questions & comments.

My biggest concern is the fact that this adds 70MB of JAR files to API. This is an extraordinary amount of new code for a single feature that will be used rarely. Were there any other options that would reduce the dependencies? Can any dependencies be excluded? If we can't slim it down, we may need to consider pushing the Graph API transport and dependencies into a separate module.

We could trim ~10MB by cutting azure-identity, but that would require us to handle token management - not a significant saving. I am leaning toward pushing these changes to a standalone module for now, then we can revisit moving it into premiumModules down the road. Thoughts?

…er class to a new module in premiumModules. Rename test action to 'GraphEmailTestWithAttachmentAction'. Remove setting activeProvider in setSession. Use StringUtilsLabKey.joinWithConjunction()
Copy link
Contributor

@labkey-adam labkey-adam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see a few suggestions and questions. I'm approving and no further review is needed at this stage.

I do think we should consider some refactoring to make key classes less tied to SMTP and Jakarta Mail. MailHelper special cases SMTP in a few places, making some of the methods applicable to SMTP only. It's now odd that ViewMessage and MultipartMessage extend Jakarta Mail classes and hold an SMTP session, even when used with Graph. Ideally, MailHelper and the message classes would be completely generic to the provider. Dumbster could construct & configure an SmtpTransportProvider, and set that as MailHelper's active provider. However, I don't think any of this is required right now. I'd prefer to get this tested and merged, and get the client to test it. Then circle back with some improvements as a follow-on.

{
_session = DEFAULT_SESSION;
}
return _smtpProvider.getSession();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this return null if _smtpProvider is not configured?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_smtpProvider is a static final field initialized at class load time, and _smtpProvider.getSession() returns null if the smtp properties are not set (otherwise returns a session if smtp properties are set).

public static Session getSession()
{
return _session;
return new MultipartMessage(getSession());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens here if getSession() returns null?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getSession() returns null when SMTP is not configured, which would cause a failure at send time.
The Graph transport doesn't call this method since it doesn't have a concept of Jakarta Mail Session. Since this only relevant for SMTP transport, renaming the method to getSmtpSession() as you suggested clarifies things.

return session;
public static void setSession(Session session)
{
_smtpProvider.setSession(session);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this throw if called when Graph transport is active?

}

return session;
public static void setSession(Session session)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarify method name? setSmtpSession()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

* Returns the SMTP session for creating messages
*/
@Nullable
public static Session getSession()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarify? getSmtpSession()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@labkey-bpatel
Copy link
Contributor Author

Please see a few suggestions and questions. I'm approving and no further review is needed at this stage.

I do think we should consider some refactoring to make key classes less tied to SMTP and Jakarta Mail. MailHelper special cases SMTP in a few places, making some of the methods applicable to SMTP only. It's now odd that ViewMessage and MultipartMessage extend Jakarta Mail classes and hold an SMTP session, even when used with Graph. Ideally, MailHelper and the message classes would be completely generic to the provider. Dumbster could construct & configure an SmtpTransportProvider, and set that as MailHelper's active provider. However, I don't think any of this is required right now. I'd prefer to get this tested and merged, and get the client to test it. Then circle back with some improvements as a follow-on.

Yup, that makes sense! I thought about decoupling SMTP specific things out MailHelper and Dumbster; however the scope kept increasing so left it as-is, but a follow up improvements sounds like a fine plan!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants