Is there an example on how to do “Modern Authentication” (Oauth) using the EWSClient in Java?
You can use AzureROPCTokenProvider class described in the MS Graph.
Code sample to initialize credentials:
ITokenProvider provider = new AzureROPCTokenProvider(oauth2.Tenant, oauth2.ClientId, "", oauth2.userNameEmail, oauth2.userPassword,
new String[] { "https://outlook.office.com/EWS.AccessAsUser.All" });
NetworkCredential credentials = new OAuthNetworkCredential(oauth2.userNameEmail, provider);
Setting the Azure “Office 365 Exchange Online”/full_access_as_app permission:
-
Application/Manage/API permissions/ + Add a permission
-
Select “APIs my organization uses” tab and search for “Office 365 Exchange Online” as shown in this image : image1.png (24.1 KB)
-
Select “Application permissions” and enable “full_access_as_app” as shown in this image : image2.png (46.7 KB)
MS Documentation link:
Access Mail Services using OAuth
Hope this helps you.
Hi, thank you for the hints. This is what I already implemented. If I use the example for the AzureROPCTokenProvider as is, I receive the following error:
java.lang.IllegalAccessError: Operation failed: 401/Unauthorized
Details:
{2}{"error":"invalid_client","error_description":"AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'.\r\nTrace ID: 0c8b5597-e1f6-49f0-811a-7bc007c40a00\r\nCorrelation ID: 3af60d7d-1837-4f14-b149-7f1f1434dcfb\r\nTimestamp: 2022-08-03 10:40:06Z","error_codes":[7000218],"timestamp":"2022-08-03 10:40:06Z","trace_id":"0c8b5597-e1f6-49f0-811a-7bc007c40a00","correlation_id":"3af60d7d-1837-4f14-b149-7f1f1434dcfb","error_uri":"https://login.microsoftonline.com/error?code=7000218"}
If I add the client secret to the body parameters, I end up in a timeout with no useful information about the cause.
But this method (with adding the client_secret in the AzureROPCTokenProvider) works for IMAP using the Sign in to Outlook scope.
We have logged this problem in our issue tracking system as EMAILJAVA-35085. You will be notified via this forum thread once this issue is resolved.
We apologize for your inconvenience.
We suggest you to use MSAL Java lib to acquire Token:
https://mvnrepository.com/artifact/com.microsoft.azure/msal4j/1.13.2
Please check the following sample code from Microsoft site:
private static IAuthenticationResult acquireTokenUsernamePassword(PublicClientApplication pca,
Set<String> scope,
IAccount account,
String username,
String password) throws Exception {
IAuthenticationResult result;
try {
SilentParameters silentParameters =
SilentParameters
.builder(scope)
.account(account)
.build();
// Try to acquire token silently. This will fail on the first acquireTokenUsernamePassword() call
// because the token cache does not have any data for the user you are trying to acquire a token for
result = pca.acquireTokenSilently(silentParameters).join();
System.out.println("==acquireTokenSilently call succeeded");
} catch (Exception ex) {
if (ex.getCause() instanceof MsalException) {
System.out.println("==acquireTokenSilently call failed: " + ex.getCause());
UserNamePasswordParameters parameters =
UserNamePasswordParameters
.builder(scope, username, password.toCharArray())
.build();
// Try to acquire a token via username/password. If successful, you should see
// the token and account information printed out to console
result = pca.acquireToken(parameters).join();
System.out.println("==username/password flow succeeded");
} else {
// Handle other exceptions accordingly
throw ex;
}
}
return result;
}
public static String getAccessToken() throws Exception {
Set<String> scope = new HashSet<String>();
scope.add("https://outlook.office365.com/.default");
String username = "test1@onmicrosoft.com";
String password = "userPass";
String clientId = "xxxxxb-f4be-4e2e-95dd-7aa4f5dxxxxx";
String tenantId = "xxxxx65f-f7e3-4bc3-841f-13b29xxxxx";
String authority =
"https://login.microsoftonline.com/" + tenantId + "/oauth2/v2.0/token";
PublicClientApplication pca = PublicClientApplication.builder(clientId)
.authority(authority)
.build();
//Get list of accounts from the application's token cache, and search them for the configured username
//getAccounts() will be empty on this first call, as accounts are added to the cache when acquiring a token
Set<IAccount> accountsInCache = pca.getAccounts().join();
IAccount account = getAccountByUsername(accountsInCache, username);
//Attempt to acquire token when user's account is not in the application's token cache
IAuthenticationResult result = acquireTokenUsernamePassword(pca, scope, account, username, password);
System.out.println("Account username: " + result.account().username());
System.out.println("Access token: " + result.accessToken());
System.out.println("Id token: " + result.idToken());
System.out.println();
accountsInCache = pca.getAccounts().join();
account = getAccountByUsername(accountsInCache, username);
//Attempt to acquire token again, now that the user's account and a token are in the application's token cache
result = acquireTokenUsernamePassword(pca, scope, account, username, password);
System.out.println("Account username: " + result.account().username());
System.out.println("Access token: " + result.accessToken());
System.out.println("Id token: " + result.idToken());
return "";
}
private static IAccount getAccountByUsername(final Set<IAccount> accountsInCache, final String username) {
if (accountsInCache.isEmpty()) {
System.out.println("==No accounts in cache");
} else {
System.out.println("==Accounts in cache: " + accountsInCache.size());
for (IAccount account : accountsInCache) {
if (account.username().equals(username)) {
return account;
}
}
}
return null;
}