I am trying to build a Java code to create users in AAD using MSAL and MS Graph API. Below is the code that I am using to create the user. I am able to retrieve the token successfully, however getting exception while trying to POST the request. What am I doing wrong?
public static void main(String[] args) throws Exception {
Map<String,Object> params = new LinkedHashMap<>();
params.put("givenName", "Test");
params.put("displayName", "ABC");
params.put("accountEnabled", true);
params.put("mailNickname","abc");
params.put("userPrincipalName","jcooper#demo.onmicrosoft.com");
StringBuilder postData = new StringBuilder();
for (Map.Entry<String,Object> param : params.entrySet()) {
if (postData.length() != 0) postData.append('&');
postData.append(URLEncoder.encode(param.getKey(), "UTF-8"));
postData.append('=');
postData.append(URLEncoder.encode(String.valueOf(param.getValue()), "UTF-8"));
}
byte[] postDataBytes = postData.toString().getBytes("UTF-8");
int length =postDataBytes.length;
URL url = new URL("https://graph.microsoft.com/v1.0/users");
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type","application/json");
conn.setRequestProperty("Authorization", "Bearer "+accessToken);
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setInstanceFollowRedirects(false);
conn.setRequestProperty("Content-Length",Integer.toString(length));
conn.connect();
conn.getInputStream();
try (var wr = new DataOutputStream(conn.getOutputStream())) {
wr.write(postDataBytes);
}
StringBuilder content;
System.out.println(postDataBytes+" "+postData);
try (var br = new BufferedReader(
new InputStreamReader(conn.getInputStream()))) {
String line;
content = new StringBuilder();
while ((line = br.readLine()) != null) {
content.append(line);
content.append(System.lineSeparator());
}
}
System.out.println(content.toString());
}
Exception : Exception in thread "main" java.io.IOException: Server returned HTTP response code: 411 for URL: https://graph.microsoft.com/v1.0/users
According to some test, I met the same issue with yours. It seems the code is correct but do not know why it still show 411 error. It may be caused by the graph api can just accept json request body but you convert the request body to application/x-www-form-urlencoded in your first part of code(I'm not sure because I test the code with json request body but still show 411).
Since you mentioned use MSAL to get access token, you can also continue to use MSAL to create the user. Please refer to this example:
GraphServiceClient graphClient = GraphServiceClient.builder().authenticationProvider( authProvider ).buildClient();
User user = new User();
user.accountEnabled = true;
user.displayName = "Adele Vance";
user.mailNickname = "AdeleV";
user.userPrincipalName = "AdeleV#contoso.onmicrosoft.com";
PasswordProfile passwordProfile = new PasswordProfile();
passwordProfile.forceChangePasswordNextSignIn = true;
passwordProfile.password = "xWwvJ]6NMw+bWH-d";
user.passwordProfile = passwordProfile;
graphClient.users()
.buildRequest()
.post(user);
For accessing Microsoft Graph from a desktop app, I'd use the InteractiveBrowserCredentialBuilder() with the TokenCredentialAuthProvider that comes with GraphSDK to get the Graph token. Check out the great sample code here. All you'd need to do to customize this is to change the last line and set the scopes differently based on what Graph API you need to call. There's a link on the bottom of that page that'll teach you to register your app properly.
The simplest way in a web app is to use Azure AD Spring Boot Starter to get an access token for a logged-in user, and use GraphSDK to call Graph in a Spring 5 web app. See this sample that demonstrates this along with full instructions (Relevant Graph code is is in SampleController.java and Utilities.java)
Related
I followed this guide to create my own REST API. I am trying to consume my API that I built from the guide but I ran into some trouble when it came to using any request that wasn't a GET request. When I tried doing a delete request. (http://localhost:8080/api/v1/employees/3)
I would get a 405 error and I'm not sure why (I do not have any password protection in my local host). I want to understand how I can create requests other than GET. I tried using query parameters for my POST request, but it was unsuccessful.
I looked at all the other StackOverFlow Similar Questions and I couldn't find anything.
EDIT1: I am using a simple Java Application to do this.
This was the code I used in order to do my GET requests
String urlString = "http://localhost:8080/api/v1/employees";
try {
String result = "";
URL url = new URL(urlString);
URLConnection conn = url.openConnection();
BufferedReader rd = new BufferedReader (new InputStreamReader(conn.getInputStream()));
String line;
while ((line = rd.readLine()) != null) {
result += line;
}
rd.close();
System.out.println(result);
}
Try to replace this URLConnection conn = url.openConnection();
to this:
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
you can use org.springframework.web.client.RestTemplate (rest-template) to consume rest api.
for delete, you can do something like
private void deleteEmployee() {
Map < String, String > params = new HashMap < String, String > ();
params.put("id", "1");
RestTemplate restTemplate = new RestTemplate();
restTemplate.delete(DELETE_EMPLOYEE_ENDPOINT_URL, params);
}
please check https://www.javaguides.net/2019/06/spring-resttemplate-get-post-put-and-delete-example.html and https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html and https://www.baeldung.com/rest-template
hope these provide enough info
I am writing a Java class to access a third-party public REST API web service, that is secured using a specific APIKey parameter.
I can access the required Json array, using the JsonNode API when I save the json output locally to a file.
E.g.
JsonNode root = mapper.readTree(new File("/home/op/Test/jsondata/loans.json"));
But, if I try to use the live secured web URL with JsonNode
E.g.
JsonNode root = mapper.readTree(url);
I am getting a:
com.fasterxml.jackson.core.JsonParseException: Unexpected character ('<' (code 60))
which suggests that I have a type mismatch. But I am assuming that it is more likely to be a connection issue.
I am handling the connection to the REST service:
private static String surl = "https://api.rest.service.com/xxxx/v1/users/xxxxx/loans?apikey=xxxx"
public static void main(String[] args) {
try {
URL url = new URL(surl);
JsonNode root = mapper.readTree(url);
....
}
I have also tried to use:
URL url = new URL(surl);
HttpURLConnection httpcon = (HttpURLConnection) url.openConnection();
InputStream isr = httpcon.getInputStream();
JsonNode root = mapper.readTree(isr);
with the same result.
When I remove the APIKey I receive a Status 400 Error. So I think I must not be handling the APIKey parameter.
Is there way to handle the call to the secured REST service URL using JsonNode? I would like to continue to use the JsonNode API, as I am extracting just two key:value pairs traversing multiple objects in a large array.
Just try to simply read response into string and log it to see what's actually going on and why you do not receive JSON from server.
URL url = new URL(surl);
HttpURLConnection httpcon = (HttpURLConnection) url.openConnection();
InputStream isr = httpcon.getInputStream();
try (BufferedReader bw = new BufferedReader(new InputStreamReader(isr, "utf-8"))) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = bw.readLine()) != null) { // read whole response
sb.append(line);
}
System.out.println(sb); //Output whole response into console or use logger of your choice instead of System.out.println
}
I have a dialogflow project that I'm trying to access from Java with a rest call.
It is giving me an authentication issue.
I have followed all online instructions (and many forum suggestions) to no avail.
I have tried generating the key json, as per the instructions here:
https://dialogflow.com/docs/reference/v2-auth-setup
and setting my environment variable as described, but nothing seems to work.
I have checked my projectID, and am running off the same machine with the environment variable, and have double, triple and quadruple checked it's name and location, but I still get the following error:
java.net.HttpRetryException: cannot retry due to server authentication, in streaming mode
Here is my code (though it's a REST call, so I don't know if it's so relevant):
String url = https://dialogflow.googleapis.com/v2/projects/MYPROJECT/agent/sessions/SESSION_NUM:detectIntent
URL url = new URL(full_url);
String inText = "Hello World";
String outText = "";
HttpURLConnection con = (HttpURLConnection) url.openConnection();
try {
con.setDoOutput(true);
con.setRequestMethod("POST");
// set body of http post
Map<String,String> arguments = new HashMap<>();
JSONObject inTextJsn = new JSONObject();
inTextJsn.append("text",inText);
inTextJsn.append("languageCode","en");
JSONObject fieldJsn = new JSONObject();
fieldJsn.append("text", inTextJsn);
arguments.put("queryInput", fieldJsn.toString());
StringJoiner sj = new StringJoiner("&");
for(Map.Entry<String,String> entry : arguments.entrySet())
sj.add(URLEncoder.encode(entry.getKey(), "UTF-8") + "="
+ URLEncoder.encode(entry.getValue(), "UTF-8"));
// post http post as bytes
byte[] bytes_out = sj.toString().getBytes(StandardCharsets.UTF_8);
con.setFixedLengthStreamingMode(bytes_out.length);
con.connect();
try (OutputStream os = con.getOutputStream()) {
os.write(bytes_out);
}
BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream(),
"UTF-8"));
// read all lines to a string
String line;
String response = "";
while ((line = reader.readLine()) != null) {
response += line;
}
JSONObject responseJsn = new JSONObject(response);
outText = responseJsn.get("fulfillmentText").toString();
} catch (Exception e) {
System.err.println(e);
} finally {
con.disconnect();
}
return restResponse;
The gist of the code is to simply send a message ("Hello World!") to my dialogflow, and get back my agent's response (the code may have bugs - it's a bit hard to test when I can't get passed this authentication issue, so please help with the authentication, not code bugs).
Thanks all!
The directions at that page assume you're going to use the gcloud program to generate a currently valid bearer token, which is then sent along with the HTTP headers. That page illustrates
Your code doesn't seem to be generating an Authorization HTTP header at all, which is why you're getting the error you do.
Since you're using Java, you should look at the google-auth-library-java library, which will give you the tools to generate the token you need to provide in the Authorization header.
You may also wish to check out the google-cloud-java library. This contains Java classes to directly perform operations against Dialogflow instead of coding the REST/HTTP calls yourself. (However, it is still at an Alpha level for Dialogflow, so may not be stable or forwards compatible.)
The problem
In my Android app, I am trying to add a user to the /_security document of my CouchDB database via HTTP PUT. If I am trying to do this by authenticating my admin user using Cookie-authentication or by simply inserting the admin data into the url like in the following, I receive an error.
URL on which the PUT is directed (if not using Cookie-authentication):
http://admin_name:admin_password#url:port/databasename/_security
Error I receive in both cases:
Authentication error: Unable to respond to any of these
challenges: {} {"error":"unauthorized","reason":"You are not a db or
server admin."}
If I am doing this via command-line using curl, the user is inserted without any problems:
~$ curl -X PUT http://admin:pw#ip:port/databasename/_security -d '{"admins":{"names":[],"roles":[]},"members":{"names":["participant_1"],"roles":[]}}'
> {"ok":true}
My aproach
As soon as I authenticate using the "Authorization" option in one of my HTTP PUT's headers, athentication is no problem anymore.
private boolean putJSONWithAuthentication(String userName, String password, String json, String url) {
// url = http://url:port/databasename/_security
// json = {"admins":{"names":[],"roles":[]},"members":{"names":["participant_1"],"roles":[]}}
HttpClient client = new DefaultHttpClient();
HttpPut put = new HttpPut(url);
String authenticationData = userName+":"+password;
String encoding = Base64.encodeToString(authenticationData.getBytes(Charset.forName("utf-8")), Base64.DEFAULT);
try {
StringEntity stringEntity = new StringEntity(json,"utf-8");
put.setEntity(stringEntity);
put.setHeader("Content-type", "application/json; charset=utf-8");
put.setHeader("Accept", "application/json");
put.setHeader("Authorization", "Basic " + encoding);
HttpResponse response = client.execute(put);
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
String line = "";
while ((line = rd.readLine()) != null) {
System.out.println(line);
}
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
However, I am receiving this error:
> {"error":"bad_request","reason":"invalid_json"}
If I am inserting my user-JSON as a usual document using the above method, e.g., to http://url:port/databasename/new_document, the JSON is inserted without any errors. Consequently, I guess the JSON string should be formatted correctly.
Thus, my questions is, what am I missing here? It seems like I cannot authenticate and put data in the request body at once. How do I correctly insert a user to the /_security document of a database from code?
Instead of creating the basic auth header manually, can you try create it using UsernamePasswordCredentials, e.g.
HttpPut put = new HttpPut(url);
UsernamePasswordCredentials creds = new UsernamePasswordCredentials(userName, password);
put.addHeader( BasicScheme.authenticate(creds,"US-ASCII",false) );
...
HttpResponse response = client.execute(put);
I am trying to fetch videos from MY OWN YouTube account, so that I get the keywords/tags for each video. I'm trying to use the most simple approach for making an authenticated call to get my videos with keywords/tags.
Here is my Java code:
String clientID = "XXXXXXXXXXXX.apps.googleusercontent.com";
String devKey = "MY-DEVELOPER-KEY";
String userEmail = "MY-GMAIL-EMAIL";
String userPassword = "MY-GMAIL-PASSWORD";
String authorName = "MY-YOUTUBE-ACCOUNT-NAME";
String url = "https://gdata.youtube.com/feeds/api/videos";
YouTubeService service = new YouTubeService( clientID, devKey );
service.setUserCredentials( userEmail, userPassword );
YouTubeQuery query = new YouTubeQuery( new URL( url ) );
query.setAuthor( authorName );
VideoFeed videoFeed = service.query( query, VideoFeed.class );
Please, help me understand what I am doing wrong, to authenticate and get those media keywords.
If you are going to refer me to another authentication option, please, show an example of using that other option for my specific scenario.
You're running into the behavior described in this blog post: your API calls are going against the search index, and those results will never have the keywords in them.
There's an example that shows how you could request the uploads feed in Java using v2 of the Data API; you can modify that example to use the channel name default instead of username and you'll automatically pull in the uploads feed for the currently authenticated account.
Here is what finally worked for me to get JSON-C feed with tags (keywords):
/** AUTHENITICATION **/
// HTTP connection
URL url = new URL("https://www.google.com/accounts/ClientLogin");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setDoInput(true);
urlConnection.setDoOutput(true);
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
// Form the POST parameters
StringBuilder content = new StringBuilder();
content.append("Email=").append(URLEncoder.encode("MY-GMAIL-LOGIN", "UTF-8"));
content.append("&Passwd=").append(URLEncoder.encode("MY-GMAIL-PASSWORD", "UTF-8"));
content.append("&service=").append(URLEncoder.encode("youtube", "UTF-8"));
OutputStream outputStream = urlConnection.getOutputStream();
outputStream.write(content.toString().getBytes("UTF-8"));
outputStream.close();
// Check response status
int responseCode = urlConnection.getResponseCode();
if( responseCode != HttpURLConnection.HTTP_OK ) {
// EXCEPTION
}
// Get the token from the response
String token;
InputStream inputStream = urlConnection.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));
String line = null;
while((line = in.readLine()) != null) {
if ( line.indexOf("Auth=") > -1 ) {
token = line.split("=")[1];
}
}
/** JSON-C FEED WITH TAGS **/
HttpClient client = new HttpClient();
GetMethod method = new GetMethod("http://gdata.youtube.com/feeds/api/users/default/uploads?v=2&alt=jsonc&max-results=50&start-index=1");
// set the authentication headers
method.setRequestHeader("Authorization", "GoogleLogin auth=" + token);
method.setRequestHeader("X-GData-Key", "key=MY-DEV-KEY");
method.setRequestHeader("GData-Version", "2");
method.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
// Make the call
int statusCode = client.executeMethod(method);
if ( statusCode != HttpStatus.SC_OK ) {
// EXCEPTION
}
String JSON = method.getResponseBodyAsString();