Reputation: 71
I'm using Java 1.8, Jakarta email (version 2.1.0) to connect to Microsoft 365 using OAuth for authentication with SMTP. Client has required to use Oauth authenticate, not basic smtp authentification. After reading of documentation https://learn.microsoft.com/en-us/exchange/client-developer... and lots other resources I have configured AD to this permissions:
Microsoft Graph offline_access
Microsoft Graph User.Read
Microsoft Graph Mail.Send
Microsoft Graph openid
Microsoft Graph IMAP.AccessAsUser.All
Microsoft Graph SMTP.Send
Office 365 Exchange Online full_access_as_app
Office 365 Exchange Online POP.AccessAsApp
Office 365 Exchange Online Mail.Send
Office 365 Exchange Online IMAP.AccessAsApp
Activated SMTP client authentication using PowerShell on Azure https://learn.microsoft.com/en-us/exchange/clients...
PS C:\Users\dx-2102> Get-TransportConfig | Format-List SmtpClientAuthenticationDisabled
SmtpClientAuthenticationDisabled : False
implemented code for send email (Java):
Properties prop = new Properties();
prop.put("mail.smtp.auth", "true");
prop.put("mail.smtp.starttls.enable", "true");
prop.put("mail.smtp.host", emailSettings.getSmtp().getHostname());
prop.put("mail.smtp.port", emailSettings.getSmtp().getPort());
prop.put("mail.debug", "true");
prop.put("mail.debug.auth", "true");
prop.put("mail.smtp.auth.xoauth2.disable", "false");
prop.put("mail.smtp.auth.mechanisms", "XOAUTH2");
prop.put("mail.transport.protocol", "smtp");
prop.put("mail.smtp.auth.login.disable", "true");
prop.put("mail.smtp.auth.plain.disable", "true");
session = Session.getInstance(prop);
session.setDebug(true);
String accessToken = getOAuth2AccessToken();
transport = session.getTransport("smtp");
transport.connect(emailSettings.getSmtp().getHostname(), emailSettings.getSmtp().getPort(), emailSettings.getSmtp().getUsername(), tokenForSmtp(emailSettings.getSmtp().getUsername(), accessToken));
/* -- */
transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
transport.close();
method for calling/getting token from Azure AD, in response we get a token with expiration time.
String url = "https://login.microsoftonline.com/" + Tenant_ID + "/oauth2/token";
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("grant_type", "client_credentials");
map.add("response_type", "code");
map.add("client_id", ClientId);
map.add("client_secret", ClientSecret);
map.add("scope","openid offline_access https%3A%2F%2Foutlook.office365.com%2FSMTP.Send ");
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<AzureResponse> response = restTemplate.postForEntity(url, map, AzureResponse.class);
method for preparing token for sending within the SMTP send-mail process
private String tokenForSmtp(String userName, String accessToken) {
final String ctrlA=Character.toString((char) 1);
final String coded= "user=" + userName + ctrlA+"auth=Bearer " + accessToken + ctrlA+ctrlA;
return Base64.getEncoder().encodeToString(coded.getBytes());
//base64("user=" + userName + "^Aauth=Bearer " + accessToken + "^A^A")
}
After send of SMTP email I receive error:
AUTH XOAUTH2 dXNlcj1zb2ZhQHNvbHV0aW9uZmFjdG9yeWFnLm9ub...=
535 5.7.3 Authentication unsuccessful [VI1PR0202CA0024.eurprd02.prod.outlook.com]
Error on sending email: 535 5.7.3 Authentication unsuccessful [VI1PR0202CA0024.eurprd02.prod.outlook.com]
Do I need a some other token scope's from Azure AD other then openid, offline_access and https://outlook.office.com/SMTP.Send ? Or do I miss something else in the configuration of Azure? Of someone has a Java example of how it should be done, please feel free to attach it. Also if you have some screenshots of what to set up on the Azure account.
Upvotes: 7
Views: 4843
Reputation: 86
I managed to do this using a combination of Azure.Identity and Microsoft.Graph SDKs. I will try to give you a step-by-step procedure.
Create App Registration in Azure Portal
You can follow the documentation from microsoft learn. Make sure the app is granted the necessary permissions you can do in Azure Portalthat via [Microsft Entra ID]-> [App Registrations] -> [Click the app you registered]-> [Manage]-> [API permissions] -> [Add new] Search for Microsoft Graph -> Mail.Send (As any user).
Add Necessary Packages to Your Java Application
dependencies{
implementation 'com.microsoft.graph:microsoft-graph:6.27.0'
implementation 'com.azure:azure-identity:1.15.0'
}
azure:
client-id: 'Your Client Id'
client-secret: 'Client Secret'
tenant-id: 'Ternant Id'
scope: 'https://graph.microsoft.com/.default'
@Configuration
@ConfigurationProperties(prefix = "azure")
@Data
public class AzureConfig {
private String clientId;
private String clientSecret;
private String tenantId;
private String scope;
@Bean
public ClientSecretCredential clientSecretCredential() {
return new ClientSecretCredentialBuilder()
.clientId(clientId)
.clientSecret(clientSecret)
.tenantId(tenantId)
.build();
}
@Bean
public GraphServiceClient graphServiceClient() {
return new GraphServiceClient(clientSecretCredential(), scope);
}
}
@Service
public class SimpleMailSender {
@Autowired
private graphServiceClient graphServiceClient;
private final String mailFrom = "[email protected]";
public void sendMail(String recipient, String subject, String body) {
try {
// Create the email message
Message message = new Message();
message.setSubject(subject);
ItemBody mailBody = new ItemBody();
mailBody.setContentType(BodyType.Text);// or BodyType.Html
mailBody.setContent(body);
message.setBody(mailBody);
// To recipient
Recipient toRecipient = new Recipient();
EmailAddress toAddress = new EmailAddress();
toAddress.setAddress(recipient);
toRecipient.setEmailAddress(toAddress);
message.setToRecipients(List.of(toRecipient));
SendMailPostRequestBody requestBody = new SendMailPostRequestBody();
requestBody.setMessage(message);
requestBody.setSaveToSentItems(false);
graphServiceClient.users()
.byUserId(mailFrom)
.sendMail()
.post(requestBody);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Hope that helps!!
Upvotes: 1