user2618875
user2618875

Reputation: 890

Outlook OAuth2 access mails

I am following this post: Outlook RestGettingStarted. From my Java app I am trying to get AccessToken and RefreshToken. When I made Authorization code request, it ended into following error:

Sorry, but we’re having trouble signing you in. We received a bad request.

Additional technical information: Correlation ID: ed838d66-5f2e-4cfb-9223-a29082ecb26f Timestamp: 2015-08-20 10:20:09Z AADSTS90011: The 'resource' request parameter is not supported.

NOTE: URL formation is correct as per documentation.

So, I removed "resource" query parameter from my code. And redirected authorize url in browser. On user consent I got authorization code. Using this code I got AccessToken. But when I try to connect with Outlook IMAP server it failed. Java ref Link for details: Java OAuth2 But it gives me error:

[AUTHENTICATIONFAILED] OAuth authentication failed.

NOTE: I added correct scope, and user email.

Then using obtained Access Token I made Mail Rest API call to get Messages from User Inbox. It ended into following error:

HTTP response: {"error":{"code":"MailboxNotEnabledForRESTAPI","message":"REST API is not yet supported for this mailbox."}}

Can anyone help me for following:

Upvotes: 0

Views: 2965

Answers (2)

Ritik Sharma
Ritik Sharma

Reputation: 419

I faced the same problem with Java mail. You need to add service principals for your application on the Azure AD.

Find complete steps explained in Medium article Complete guide: Java Mail IMAP OAuth2.0 Connect Outlook | by Ritik Sharma | Dec, 2022.

Upvotes: -1

Travis Hummel
Travis Hummel

Reputation: 41

I ran into this recently, but don't remember which solved it. One main issue is in the documentation in that it is varying. It will tell you to attach "resource", but that is for something else like Azure.

Here is the code I used:

First request to send:

      private static final String USER_OAUTH2_AUTHORIZE_URL = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize";

      public String getOAuthDialog(Http.Request request) {
        return USER_OAUTH2_AUTHORIZE_URL
           + "?client_id=" + config.getClientId()
           + "&redirect_uri=" + getOutlookLoginRedirect(request)
           + "&response_type=code"
           + "&scope=https%3A%2F%2Foutlook.office.com%2Fmail.send%20" +
             "https%3A%2F%2Foutlook.office.com%2Fmail.readwrite%20" + 
             "offline_access%20openid%20email%20profile"
           + "&state=" + crypto.generateSignedToken();
       }

Scope was the hardest thing to figure out. I found a lot of ones that did not work. And it wasn't clear that I needed to separate them with spaces.

Then they will send you a request to your redirect url that was supplied. It will contain a code which you need to exchange for the data you requested in the scope. The redirect url that is supplied needs to be the exact same. Also you need to register the redirect url on your application portal under the Platform->Add Platform->Redirect URI->Add Url

  private static final String USER_ACCESS_TOKEN_URL = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
  private Map<String, String> sendOutlookUserOAuthRequest(Http.Request request, String code) {
    WSClient ws = WS.client();

    HttpParameters params = new HttpParameters();
    params.put("client_id", config.getClientId(), true);
    params.put("client_secret", config.getClientSecret(), true);
    params.put("code", code, true);
    params.put("redirect_uri", getOutlookLoginRedirect(request), true);
    params.put("grant_type", "authorization_code");
    String postParams = OAuthUtil.parametersToString(params);

    WSRequest wsRequest = ws.url(USER_ACCESS_TOKEN_URL)
        .setMethod("POST")
        .setContentType("application/x-www-form-urlencoded")
        .setBody(postParams);

    WSResponse wsResponse = wsRequest.execute().get(10, TimeUnit.SECONDS);

    Map<String, String> result = new HashMap<>();
    if (wsResponse.getStatus() != HttpStatus.SC_OK) {
      return result;
    }

    JsonNode node = wsResponse.asJson();
    if (node.hasNonNull("access_token")) {
      result.put("access_token", node.get("access_token").asText());
    }
    if (node.hasNonNull("refresh_token")) {
      result.put("refresh_token", node.get("refresh_token").asText());
    }

    if (node.hasNonNull("id_token")) {
      String[] tokenSplit = node.get("id_token").asText().split("\\.");
      if (tokenSplit.length >= 2) {
        try {
          JSONObject jsonObject = new JSONObject(new String(Base64.getDecoder().decode(tokenSplit[1])));
          if (jsonObject.has("name")) {
            result.put("name", jsonObject.get("name").toString());
          }
          if (jsonObject.has("email")) {
            result.put("outlookUid", jsonObject.get("email").toString());
          } else if (jsonObject.has("preferred_username")) {
            result.put("outlookUid", jsonObject.get("preferred_username").toString());
          }
        } catch (JSONException e) {
          log.error("Error extracting outlookUid from id_token: ", e);
        }
      }
    }

    return result;
  }

Another request that you might need is to update the refresh token:

  private String getAccessTokenFromRefreshToken(User user) {

    WSClient ws = WS.client();
    HttpParameters params = new HttpParameters();
    params.put("client_id", config.getClientId(), true);
    params.put("client_secret", config.getClientSecret(), true);
    params.put("grant_type", "refresh_token");
    params.put("refresh_token", user.getOutlookRefreshToken());
    String postParams = OAuthUtil.parametersToString(params);

    WSRequest wsRequest = ws.url(USER_ACCESS_TOKEN_URL)
        .setMethod("POST")
        .setContentType("application/x-www-form-urlencoded")
        .setBody(postParams);

    WSResponse wsResponse = wsRequest.execute().get(10, TimeUnit.SECONDS);
    if (wsResponse.getStatus() != HttpStatus.SC_OK) {
      log.error("Failure to refresh outlook access token for user: " + user +
          ". Received status: " + wsResponse.getStatus() + " : " + wsResponse.getStatusText());
      return null;
    }
    JsonNode node = wsResponse.asJson();
    if (node.hasNonNull("access_token")) {
      String accessToken = node.get("access_token").asText();
      return accessToken;
    } else {
      log.error("Outlook refresh token failure, 'access_token' not present in response body: " + wsResponse.getBody());
      return null;
    }
  }

One issue I ran into that took far longer than I would have hoped was in getting the clientId and clientSecret. This was because the language microsoft uses wasn't the most explicit. Client Id and application id are used interchangeably. The client secret is also the password that you create on the Application Portal, not to be confused with the Private Key that you can generate.

So you actually want the application_id and the password, although they refer to them as client_id and client_secret with no direct indication as to the lines drawn.

This is all assuming you have set up an application on the Outlook Application Portal. https://apps.dev.microsoft.com/

I hope this helps, although I assume you probably already solved this.

Upvotes: 1

Related Questions