3

I am writing application that need to read mailbox using IMAP, but as daemon, without user interaction. I need to use OAuth2 to get access. Because I need it without user interaction, I need to use client credentials flow. This was added this June.

I have done everything from official documentation. Registered application, added permissions, added mailbox permission using PowerShell.

When I get request access token with scope https://outlook.office365.com/.default, the one that I receive has role IMAP.AccessAsApp, so I believe that is correct. I used https://jwt.ms/ to parse JWT.

The problem is when I try to authenticate using this access token in Java, for example

    Properties props = new Properties();
    props.put("mail.imap.ssl.enable", "true");
    props.put("mail.imap.auth.mechanisms", "XOAUTH2");
    props.put("mail.debug", "true");

    Session session = Session.getInstance(props);
    Store store = session.getStore("imap");
    store.connect("outlook.office365.com", 993, "testing@mydomain.com", "accessToken");

I receive AUTHENTICATE failed. I tried same code with access token received using authorization code flow, which requires user interaction. Using that access code I was able to connect to mailbox. So the code is correct.

I even tried using client id and service id instead of email address as username, but without success.

I am not sure where I made the mistake and if I am using correct username. Any help is appreciated.

Boris
  • 726
  • 1
  • 10
  • 22
  • Which version of javamail are you using? Are you sure you're using an Access Token and not a Rrefresh Token, and that your access token hasn't expired? What is in the response to AUTHENTICATE failed? Turn on debugging, there should be some base64 error message in the protocol flow. – Max Jul 27 '22 at 16:25
  • My setup is also exactly as OP describes and I cannot get it to work either. In my case, I'm definitely using an access token and the token is not expired. It is newly created. Alot of people eem to be experiencing the exact same issue. See: https://techcommunity.microsoft.com/t5/office-365/authentication-failure-for-imap-and-pop3-using-client-credential/m-p/3583468/highlight/false#M39544 – jamie Jul 27 '22 at 18:21
  • When add scope in Azure GUI, the scope https://graph.microsoft.com/IMAP.AccessAsUser.All is added. This does not correspond with https://outlook.office365.com/.default. A possible cause of auth failure? The resolutiion would be to somehow add scope https://outlook.office365.com/IMAP.AccessAsUser.All to Azure app registration. – jamie Jul 27 '22 at 19:26
  • Hi, I am using Java Mail 1.6.7. And the token is valid and non expired. @jamie Thank you for the link. From what I understood from documentation, you need to add IMAP.AccessAsApp permission and scope https://outlook.office365.com/.default will use it, because it is defined directly on register application. Because it is request without user interaction, all permissions need to be defined on application, instead of scope on request. So I think, there is not possible to add IMAP.AccessAsUser.All to client credentials grant flow. Everything works with fine with authorization code flow. – Boris Jul 27 '22 at 20:40
  • Because everything works fine with authorization code flow I suspect that I did not related service principal correctly to mail or mail should not be used as username for access. I will try to increase debug on IMAP connection, to see if there is some additional info, because defining just debug dies not have any specific information. – Boris Jul 27 '22 at 20:42
  • Sorry, I misread the doc. Its been at this too long! I added Exchange Online scope https://ps.outlook.com/IMAP.AccessAsApp. I tried requesting https://outlook.office365.com/.default as per doc. Still authentication failed. – jamie Jul 27 '22 at 21:05

2 Answers2

2

I wrote same answer here, so I am coping it here.

I think I made some progress.

I read documentation few times, tried few times from the start with same error. I even have tried using client and object ids instead of email as username, in lack of better ideas.

So this is where I think I have made mistake previous times.

On the part where it is needed to register service principal, I needed to execute

New-ServicePrincipal -AppId <APPLICATION_ID> -ServiceId <OBJECT_ID> [-Organization <ORGANIZATION_ID>]

Here I have put enterprise application object id as ServiceId argument. And that is ok.

But on

Add-MailboxPermission -Identity "email address removed for privacy reasons" -User 
<SERVICE_PRINCIPAL_ID> -AccessRights FullAccess

I have put my registered application object id as User argument. I also tried setting object id of enterprise application, but it did not have success.

When I executed

Get-ServicePrincipal -Organization <ORGANIZATION_ID> | fl

I did not pay attention to ServiceId property, even with documentation specifying it and saying it will be different.

Now I cleared everything and started fresh.

I have executed all the steps again, but on the step for creating new service principal I used data from enterprise application view. When I need to add mail permission, I list service principals, and then use ServiceId value from the output, as argument for user.

With that, I was able to authorise.

Boris
  • 726
  • 1
  • 10
  • 22
  • Thanks again Boris. A potential gotcha. Don't make the mistake of using the Object ID from the Azure App Registration View. After creating/registering the Azure application, go to Azure->Active Directory-> Enterprise Applications->[choose enterprise app}, then use the Object ID from there. – jamie Jul 29 '22 at 18:20
0

Thanks everyone for sharing your experience. This has proved to be a little confusing. :)

To sum everything up, to access a mailbox with IMAPS and OAuth2 (as opposed to using Graph API which is another method Microsoft recommends):

  • Create an Azure App Registration
  • Add API permission Office 365 Exchange Online - IMAP.AccessAsApp and grant admin consent
  • Create a service principal, which will be used to grant mailbox permissions to in Exchange Online
Connect-AzureAD
Connect-ExchangeOnline
$azapp = Get-AzureADApplication -SearchString 'App Registration Name'
$azsp = Get-AzureADServicePrincipal -SearchString $azapp.DisplayName
# GOTCHA: You need the ObjectId from 'Enterprise applications' (Get-AzureADServicePrincipal), not 'Application registrations' (Get-AzureADApplication) for ServiceId (thanks @[jamie][1])
$sp = New-ServicePrincipal -AppId $azapp.AppId -ServiceId $azsp.ObjectId -DisplayName "EXO Service Principal for $($azapp.DisplayName)"
  • Grant access rights to mailboxes for the service principal
$mbxs = 'mymbx1@yourdomain.tld',`
        'mymbx2@yourdomain.tld',`
        'mymbx3@yourdomain.tld'
$mbxs | %{ Add-MailboxPermission -Identity $_ -User $sp.ServiceId -AccessRights FullAccess } | fl *
Get-MailboxPermission $mbxs[-1] | ft -a

You can use Get-IMAPAccessToken.ps1 to test your setup

  • .\Get-IMAPAccessToken.ps1 -TenantID $TenantId -ClientId $ClientId -ClientSecret $ClientSecret -TargetMailbox $TargetMailbox

Other parameters you may need:

  • Authority: https://login.microsoftonline.com/<YourTenantId>/
  • Scope: https://outlook.office365.com/.default
Inny
  • 3
  • 4