Microsoft exchange - IMAP OAuth2 With client credentials

Viewed 850

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.

2 Answers

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.

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
Related