OfficeDev / ews-java-api

A java client library to access Exchange web services. The API works against Office 365 Exchange Online as well as on premises Exchange.
MIT License
870 stars 560 forks source link

How to Authenticate an EWS application by using OAuth? #748

Open Chandler54321 opened 2 years ago

Chandler54321 commented 2 years ago

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 commented 2 years ago

`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 401 Exception 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`

davidmoten commented 2 years ago

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

Chandler54321 commented 2 years ago

`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

davidmoten commented 2 years ago

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 commented 2 years ago

Triple ticks please, formatting gets stuffed up.

davidmoten commented 2 years ago

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

Egis-Devandrin commented 2 years ago

@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%

davidmoten commented 2 years ago

@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));
Rakshana113 commented 2 years ago

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!

internationalJoke commented 2 years ago

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

internationalJoke commented 2 years ago

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.