Microsoft outlook authentication oAuth2.0

807 views Asked by At

We have a daemon application that makes IMAP connection to access mailbox of user. Earlier we were using plain authentication method of using email ID and password to establish IMAP connection. Now as Microsoft has blocked this type authentication process and introduced oAuth2.0.

My question here I was able to establish IMAP connection with the user that falls inside my tenant. But I am unable to figure out that how it can be done if I need to access the mailbox of user that doesn't fall inside my tenant or need to access the mailbox of any personal outlook account.

1

There are 1 answers

3
Rukmini On

I tried to reproduce the same in my environment and got the below results:

Note that, if you want to access the mailbox of user that doesn't fall inside your tenant or need to access the mailbox of any personal outlook account then you have to register a Multi-Tenant Azure AD Application like below:

enter image description here

I created an Azure AD Multi-Tenant Application and granted the API Permissions:

enter image description here

Now, I registered service principals in Exchange by using below commands:**

Connect-ExchangeOnline -Organization TenantID

New-ServicePrincipal -AppId AppID -ServiceId ObjectID [-Organization OrganizationID]
Get-ServicePrincipal | fl

enter image description here

I granted service principal access to one mailbox :

Add-MailboxPermission -Identity "[email protected]" -User 
ServicePrincipal_ID> -AccessRights FullAccess

Test-ApplicationAccessPolicy -Identity "[email protected]" -AppId AppID

enter image description here

I generated the access token via Postman for Multi-Tenant Application by using the parameters like below:

https://login.microsoftonline.com/common/oauth2/v2.0/token

client_id:5f3068f5-a920-4d6d-9742-XXXXXX
client_secret:ESJ8Q~ShJVdlY2MhKicyTEApGdtZh*******
scope:https://outlook.office365.com/.default
grant_type:client_credentials

enter image description here

To do the same in JAVA, you can refer the below sample code by user3206771 in this SO Thread :

public String getAccessTokenByClientCredentialGrant()  {       
    String accessToken = null;
    String clientId = "CLIENTID";
    String secret = "CLIENTSECRET";
    String authority = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
    String scope = "https://outlook.office365.com/.default";
    log.info("Client ID : "+clientId);
    log.info("Client Secret : "+secret);
    log.info("Auth Server: "+authority);
    log.info("Scope: "+scope);
        try {
              ConfidentialClientApplication app = ConfidentialClientApplication.builder(
                clientId,
              ClientCredentialFactory.createFromSecret(secret))
                .authority(authority)
                .build();   
  
        ClientCredentialParameters clientCredentialParam = ClientCredentialParameters.builder(
                Collections.singleton(scope))
                .build();
        
        CompletableFuture<IAuthenticationResult> future = app.acquireToken(clientCredentialParam);
        IAuthenticationResult result = future.get();
        accessToken = result.accessToken();
        
    } catch(Exception e) {
        log.error("Exception in acquiring token: "+e.getMessage());
        e.printStackTrace();
    }
    log.info("Access Token : "+accessToken);
    return accessToken;
}
public Store connect(String userEmailId, String oauth2AccessToken) throws Exception {
    String host = "outlook.office365.com";
    String port = "993";
    Store store = null;
     
    String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
    Properties props= new Properties();
    props.put("mail.imaps.ssl.enable", "true");
    props.put("mail.imaps.sasl.enable", "true");
    props.put("mail.imaps.port", port);
    props.put("mail.imaps.auth.mechanisms", "XOAUTH2");
    props.put("mail.imaps.sasl.mechanisms", "XOAUTH2");
    props.put("mail.imaps.auth.login.disable", "true");
    props.put("mail.imaps.auth.plain.disable", "true");
    props.setProperty("mail.imaps.socketFactory.class", SSL_FACTORY);
    props.setProperty("mail.imaps.socketFactory.fallback", "false");
    props.setProperty("mail.imaps.socketFactory.port", port);
    props.setProperty("mail.imaps.starttls.enable", "true");
    props.put("mail.debug", "true");
    props.put("mail.debug.auth", "true");
    Session session = Session.getInstance(props);
    session.setDebug(true);
    store = session.getStore("imaps");
    log.info("OAUTH2 IMAP trying to connect with system properties to Host:" + host + ", Port: "+ port
            + ", userEmailId: " + userEmailId+ ", AccessToken: " + oauth2AccessToken);
    try {
    
        store.connect(host, userEmailId, oauth2AccessToken);
        log.info("IMAP connected with system properties to Host:" + host + ", Port: "+ port
            + ", userEmailId: " + userEmailId+ ", AccessToken: " + oauth2AccessToken);
        if(store.isConnected()){
            log.info("Connection Established using imap protocol successfully !");      
        }
    } catch (Exception e) {
        log.error("Store.Connect failed with the errror: "+e.getMessage());
        StringWriter sw = new StringWriter();
        e.printStackTrace(new PrintWriter(sw));
        String exceptionAsString = sw.toString();
        log.error(exceptionAsString);
     
    }
    return store;
}
public void getEmailContents() throws Exception {
    Store store = null;
       String accessToken = getAccessTokenByClientCredentialGrant();
    String emailId = "<email which needs to be read>";
    try {
        store = connect(emailId, accessToken );
    } catch (Exception ex) {
        log.error("Exception in connecting to email " + ex.getMessage());
        ex.printStackTrace();
        }
   }