1

I need to authenticate against OAuth2.0 Microsoft Dynamics CRM from a background Java application; background because it's an integration app between the ERP of the customer and its Dynamics online instance.

I tried to use spring-security-oauth2 classes to get an high level set of resource to handle authentication, but i can't retrieve the initial token, while I'm successful if I try with building "manually" the http requests needed.

I wrote a simple Java application to test the authentication and I had this piece of code working, with content that is the String representation of the access token JSon:

    String accessTokenURL = "https://login.microsoftonline.com/common/oauth2/token";
    CloseableHttpClient client = HttpClients.createDefault();
    HttpPost requestToken = new HttpPost(accessTokenURL);

    requestToken.addHeader("Cache-Control", "no-cache");
    requestToken.addHeader("Content-Type", "application/x-www-form-urlencoded");

    List<NameValuePair> params = new ArrayList<NameValuePair>();
    params.add(new BasicNameValuePair("grant_type", "password"));
    params.add(new BasicNameValuePair("client_id", clientId));
    params.add(new BasicNameValuePair("resource", resource));
    params.add(new BasicNameValuePair("username", username));
    params.add(new BasicNameValuePair("password", password));
    params.add(new BasicNameValuePair("client_secret", clientSecret));
    requestToken.setEntity(new UrlEncodedFormEntity(params));

    CloseableHttpResponse response = client.execute(requestToken);
    InputStream is = response.getEntity().getContent();
    String content = IOUtils.toString(is);
    System.out.println(content);
    client.close();

resource is the Dynamics online instance of the customer.

I tried something similar using Spring Security OAuth2 client classes but I always get "401 Unauthorized":

    ResourceOwnerPasswordResourceDetails resourceObj = new ResourceOwnerPasswordResourceDetails();
    resourceObj.setClientId(clientId);
    resourceObj.setClientSecret(clientSecret);
    resourceObj.setGrantType("password");
    resourceObj.setAccessTokenUri(accessTokenURLWithResource);
    // resourceObj.setId(resource);
    resourceObj.setTokenName("bearer_token");
    resourceObj.setUsername(username);
    resourceObj.setPassword(password);

    AccessTokenRequest atr = new DefaultAccessTokenRequest();
    Map<String, List<String>> headersMap = new HashMap<String, List<String>>();
    headersMap.put("Cache-Control", Arrays.asList("no-cache"));
    headersMap.put("Content-Type", Arrays.asList("application/x-www-form-urlencoded"));
    atr.add("client_id", clientId);
    atr.add("resource", resource);
    atr.add("client_secret", clientSecret);
    atr.add("username", username);
    atr.add("password", password);
    OAuth2ClientContext context = new DefaultOAuth2ClientContext(atr);
    OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceObj, context);
    OAuth2AccessToken token = restTemplate.getAccessToken();
    System.out.println(new Gson().toJson(token));

I tried using different ways to pass the access token URL and resource but the result is always the same.

Any help or any other advice about other high level library to be used in this case are appreciated, thanks.

  • 1
    What is `accessTokenURLWithResource` (<= this should point to the token-endpoint)? Furthermore - the defaultAccessTokenRequest including the headers is not needed. Just create a `new OAuth2RestTemplate(resourceObj)` and that's it. – fateddy Nov 21 '17 at 08:04
  • @fateddy `accessTokenURLWithResource = accessTokenURL + "?resource="+resource` and well I tried with `new OAuth2RestTemplate(resourceObj)` but I got a different error about resource not passed to the request – davide.fabbri Nov 21 '17 at 08:09
  • 1
    Oh, missed that one. Provide it as additional param using the `AccessTokenRequest`: `DefaultAccessTokenRequest tokenRequest = new DefaultAccessTokenRequest(); tokenRequest.add("resource", resource); OAuth2ClientContext context = new DefaultOAuth2ClientContext(tokenRequest); OAuth2RestTemplate template = new OAuth2RestTemplate(resourceObj, context);` – fateddy Nov 21 '17 at 12:17
  • @fateddy Well that's exactly what I posted in the original post, as I said I got 401 Unauthorized – davide.fabbri Nov 21 '17 at 13:58
  • 1
    Hmm... `client_id` etc. are already part of the ResourceDetails, no need to pass them around again. So with the additional attribute `resource` it still does not work? – fateddy Nov 21 '17 at 14:10
  • @fateddy well thanks to you I solved. In this context `client_id` `client_secret` `username` and `password` must be passed ONLY in `AccessTokenRequest` object, and not in both `AccessTokenReqest` and `ResourceOwnerPasswordResourceDetails` – davide.fabbri Nov 21 '17 at 14:17
  • 1
    Thats what I was thinking - the specification does not allow parameters to be sent more than once – fateddy Nov 21 '17 at 14:18

1 Answers1

0

Thanks to some hints of @fateddy I came to a solution. This piece of code works, now I'll try to integrate in my application

    ResourceOwnerPasswordResourceDetails resourceObj = new ResourceOwnerPasswordResourceDetails();
    // resourceObj.setClientId(clientId);
    // resourceObj.setClientSecret(clientSecret);
    resourceObj.setGrantType("password");
    resourceObj.setAccessTokenUri(accessTokenURLWithResource);
    // resourceObj.setId(resource);
    resourceObj.setTokenName("bearer_token");
    // resourceObj.setUsername(username);
    // resourceObj.setPassword(password);

    AccessTokenRequest atr = new DefaultAccessTokenRequest();
    Map<String, List<String>> headersMap = new HashMap<String, List<String>>();
    headersMap.put("Cache-Control", Arrays.asList("no-cache"));
    headersMap.put("Content-Type", Arrays.asList("application/x-www-form-urlencoded"));
    atr.add("client_id", clientId);
    atr.add("resource", resource);
    atr.add("client_secret", clientSecret);
    atr.add("username", username);
    atr.add("password", password);
    OAuth2ClientContext context = new DefaultOAuth2ClientContext(atr);
    OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceObj, context);
    OAuth2AccessToken token = restTemplate.getAccessToken();
    System.out.println(new Gson().toJson(token));
  • 1
    actually you could turn a few things around here... omit all the `AccessTokenRequest` attributes but `resource`as they are already part of the `ResourceDetails`. You also do not need the headers. – fateddy Nov 21 '17 at 14:33
  • You're right about `headersMap`, it was a test I forgot to completely remove. Setting the other parameters on `ResourceDetails` and removing them from `AccessTokenRequest` doesn't work for me, they have to be added to AccessTokenRequest, don't know if depends on the specific instance configuration – davide.fabbri Nov 21 '17 at 15:11