ews-java-api icon indicating copy to clipboard operation
ews-java-api copied to clipboard

How to Authenticate an EWS application by using OAuth?

Open Chandler54321 opened this issue 3 years ago • 11 comments

https://docs.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth How to Authenticate an EWS application by using OAuth in Java? Thanks

Chandler54321 avatar Nov 30 '21 13:11 Chandler54321

`public final class BearerTokenCredentials extends ExchangeCredentials {

private static final String BEARER_TOKEN_FORMAT_REGEX = "^[-._~+/A-Za-z0-9]+=*$";
private static final String AUTHORIZATION = "Authorization";
private static final String BEARER_AUTH_PREAMBLE = "Bearer ";
private String token;

public String getToken() {
    return token;
}

public BearerTokenCredentials(String bearerToken) {
    if (bearerToken == null) {
        throw new IllegalArgumentException("Bearer token can not be null");
    }
    this.validateToken(bearerToken);
    this.token = bearerToken;
}

protected void validateToken(String bearerToken) throws IllegalArgumentException {
    if (!bearerToken.matches(BEARER_TOKEN_FORMAT_REGEX)) {
        throw new IllegalArgumentException("Bearer token format is invalid.");
    }
}

@Override
public void prepareWebRequest(HttpWebRequest request) {
    Map<String, String> headersMap = request.getHeaders();
    String bearerValue = BEARER_AUTH_PREAMBLE + token;
    headersMap.put(AUTHORIZATION, bearerValue);
    request.setHeaders(headersMap);
}

}`

` String email = "emailaddressxx"; ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2); service.setTraceEnabled(true); BearerTokenCredentials credentials = new BearerTokenCredentials(MsEwsTokenProvider.getAccesToken()); service.setCredentials(credentials); service.setUrl(new URI("https://" + "outlook.office365.com" + "/EWS/Exchange.asmx")); service.setImpersonatedUserId(new ImpersonatedUserId(ConnectingIdType.SmtpAddress, email));

    // Bind to the Inbox.
    Folder inbox = Folder.bind(service, WellKnownFolderName.Inbox);
    int count = inbox.getTotalCount();
    System.out.println(inbox.getDisplayName());
    ItemView itemView = new ItemView(count);
    //
    FindItemsResults<Item> findResults = service.findItems(inbox.getId(), itemView);
    ArrayList<Item> items = findResults.getItems();
    for (int i = 0; i < items.size(); i++) {
        EmailMessage message = EmailMessage.bind(service, items.get(i).getId());
        message.load();
        System.out.println(message.getSender());
    }

But return 401Exception in thread "main" microsoft.exchange.webservices.data.core.exception.service.remote.ServiceRequestException: The request failed. The request failed. The remote server returned an error: (401)Unauthorized at microsoft.exchange.webservices.data.core.request.SimpleServiceRequestBase.internalExecute(SimpleServiceRequestBase.java:74) at microsoft.exchange.webservices.data.core.request.MultiResponseServiceRequest.execute(MultiResponseServiceRequest.java:158) at microsoft.exchange.webservices.data.core.ExchangeService.bindToFolder(ExchangeService.java:504) at microsoft.exchange.webservices.data.core.ExchangeService.bindToFolder(ExchangeService.java:523) at microsoft.exchange.webservices.data.core.service.folder.Folder.bind(Folder.java:98) at microsoft.exchange.webservices.data.core.service.folder.Folder.bind(Folder.java:147) at ReadExchangeMail.main(ReadExchangeMail.java:64) Caused by: microsoft.exchange.webservices.data.core.exception.service.remote.ServiceRequestException: The request failed. The remote server returned an error: (401)Unauthorized at microsoft.exchange.webservices.data.core.request.ServiceRequestBase.validateAndEmitRequest(ServiceRequestBase.java:644) at microsoft.exchange.webservices.data.core.request.SimpleServiceRequestBase.internalExecute(SimpleServiceRequestBase.java:62) ... 6 more Caused by: microsoft.exchange.webservices.data.core.exception.http.HttpErrorException: The remote server returned an error: (401)Unauthorized at microsoft.exchange.webservices.data.core.request.ServiceRequestBase.getEwsHttpWebResponse(ServiceRequestBase.java:723) at microsoft.exchange.webservices.data.core.request.ServiceRequestBase.validateAndEmitRequest(ServiceRequestBase.java:639) ... 7 more`

Chandler54321 avatar Dec 02 '21 14:12 Chandler54321

Can you show us MsEwsTokenProvider.java? By the way use triple back ticks for blocks of code (not single back ticks).

davidmoten avatar Dec 02 '21 22:12 davidmoten

`public final class MsEwsTokenProvider {

private static final String EWS_URL = "https://outlook.office365.com/EWS/Exchange.asmx";
private static String scope = "https://outlook.office.com/.default";

private static String clientId = "xxxxx";
private static String secret = "xxxxx";
private static String authority = "https://login.microsoftonline.com/xxxxx/";


public static String getAccesToken() throws Exception {

    ConfidentialClientApplication app = ConfidentialClientApplication.builder(
            clientId,
            ClientCredentialFactory.createFromSecret(secret))
            .authority(authority)
            .build();

    // With client credentials flows the scope is ALWAYS of the shape "resource/.default", as the
    // application permissions need to be set statically (in the portal), and then granted by a tenant administrator
    ClientCredentialParameters clientCredentialParam = ClientCredentialParameters.builder(
            Collections.singleton(scope))
            .build();

    CompletableFuture<IAuthenticationResult> future = app.acquireToken(clientCredentialParam);
    System.out.println(future.get().accessToken());
    return future.get().accessToken();
}`

I can got the token,but can not get the mail inbox. Thanks

Chandler54321 avatar Dec 03 '21 00:12 Chandler54321

Yeah but if you are getting a 401 it means that the token doesn't authenticate you. I guess it could be a permission problem and the service is giving you a 401 instead of the appropriate 403.

davidmoten avatar Dec 03 '21 00:12 davidmoten

Triple ticks please, formatting gets stuffed up.

davidmoten avatar Dec 03 '21 00:12 davidmoten

https://docs.github.com/en/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#quoting-code

davidmoten avatar Dec 03 '21 00:12 davidmoten

@davidmoten @Chandler54321

Any idea how to resolve it tho?

On the EWS docs, theres two types of Authentication, Delegated and App only Where delegated uses specific permissions but I keep getting a 401 unauthorised, but Authentication via Application Auth works 100%

Egis-Devandrin avatar Dec 08 '21 09:12 Egis-Devandrin

@Chandler54321 I notice that our code (that works fine) does this step:

service.setImpersonatedUserId(new ImpersonatedUserId(ConnectingIdType.PrincipalName, email));

You might want to try that instead of your line:

service.setImpersonatedUserId(new ImpersonatedUserId(ConnectingIdType.SmtpAddress, email));

davidmoten avatar Dec 08 '21 22:12 davidmoten

BearerTokenCredentials credentials = new BearerTokenCredentials(MsEwsTokenProvider.getAccesToken()); service.setCredentials(credentials); I am getting setCredentials is not applicable for BearerTokenCredentials .kindly help me out. Thanks in Advance!

Rakshana113 avatar Jul 20 '22 02:07 Rakshana113

You answer is quite helpful to me, Thanks a lot.

internationalJoke avatar Jul 27 '22 09:07 internationalJoke

BearerTokenCredentials credentials = new BearerTokenCredentials(MsEwsTokenProvider.getAccesToken()); service.setCredentials(credentials); I am getting setCredentials is not applicable for BearerTokenCredentials .kindly help me out. Thanks in Advance!

Did your BearerTokenCredentials class extend ExchangeCredentials? "public class BearerTokenCredentials extends ExchangeCredentials" works well here.

internationalJoke avatar Jul 27 '22 09:07 internationalJoke