Lync UCWA - Create application gives a HTTP 409: Conflict error - javascript

I have been trying for the past couple of days to develop an application for our Lync service at work using the UCWA API from Microsoft (a REST API). To get an application working: I first have to submit it to the API using a POST request to a certain URL. First, I have to authenticate with the server, and I do that by posting a username and a password credential to the API. I then get an access token back which I can use to make further requests to the API by posting the token inside the header of each request. I have been able to get an access token working, but when I try to register the application by posting a HTTP request to https://lyncextws.company.com/ucwa/oauth/v1/applications: Things will start to go wrong.
All this is done through one JavaScript file working with iframes to bypass the Same-origin policy.
This is what my code currently looks like:
<!DOCTYPE html>
<html lang="no">
<head>
<meta charset="UTF-8" />
<title>PresInfoDisp</title>
</head>
<body>
<iframe src="https://lyncextws.company.com/Autodiscover/XFrame/XFrame.html" id="xFrame" style="display: none;"></iframe>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
var access_token;
var stage = 0;
// CONNECT AND AUTHENTICATE WITH LYNC UCWA SERVICE
function connectAndAuthenticate() {
stage = 1;
var request = {
accepts: 'application/json',
type: 'POST',
url: 'https://lyncextws.company.com/WebTicket/oauthtoken',
data: 'grant_type=password&username=alexander#domain.company.com&password=somePassword'
};
document.getElementById('xFrame').contentWindow.postMessage(JSON.stringify(request), 'https://lyncextws.company.com/WebTicket/oauthtoken');
}
// REQUEST A USER RESOURCE
function getUserResourceAuthRequest() {
stage = 0;
var request = {
accepts: 'application/json',
type: 'GET',
url: 'https://lyncextws.company.com/Autodiscover/AutodiscoverService.svc/root/oauth/user?originalDomain=company.com'
};
document.getElementById('xFrame').contentWindow.postMessage(JSON.stringify(request), 'https://lyncextws.company.com/Autodiscover/AutodiscoverService.svc/root/oauth/user?originalDomain=company.com');
}
function getUserResource() {
stage = 2;
var request = {
accepts: 'application/json',
type: 'GET',
url: 'https://lyncextws.company.com/Autodiscover/AutodiscoverService.svc/root/oauth/user?originalDomain=company.com',
headers: {Authorization: "Bearer "+access_token}
};
document.getElementById('xFrame').contentWindow.postMessage(JSON.stringify(request), 'https://lyncextws.company.com/Autodiscover/AutodiscoverService.svc/root/oauth/user?originalDomain=company.com');
}
// REGISTER APPLICATION RESOURCE
function registerApplication() {
stage = 3;
var request = {
accepts: 'application/json',
type: 'POST',
url: 'https://lyncextws.company.com/ucwa/oauth/v1/applications',
headers: {Authorization: "Bearer "+access_token},
data: {'userAgent': 'InfoDisp1', 'endpointId' : '2d9dc28d-4673-4035-825c-feb64be28e4e', 'culture': 'en-US'}
};
document.getElementById('xFrame').contentWindow.postMessage(JSON.stringify(request), 'https://lyncextws.company.com/ucwa/oauth/v1/applications');
}
// GRAB A LIST OF CONTACTS
function listContacts() {
stage = 4;
var request = {
accepts: 'application/json',
type: 'GET',
url: 'https://lyncextws.company.com/ucwa/oauth/v1/applications',
headers: {Authorization: "Bearer "+access_token}
};
document.getElementById('xFrame').contentWindow.postMessage(JSON.stringify(request), 'https://lyncextws.company.com/ucwa/v1/applications');
}
this.receiveMessage = function(message) {
switch(stage) {
case 1:
var beforeReplace = message.data.replace("/\\/g", "");
var json = jQuery.parseJSON(beforeReplace);
var json2 = jQuery.parseJSON(json.responseText);
access_token = json2.access_token;
console.log(json2.access_token);
console.log(message);
getUserResource();
break;
case 0:
console.log(message);
connectAndAuthenticate();
break;
case 2:
var beforeReplace = message.data.replace("/\\/g", "");
var json = jQuery.parseJSON(beforeReplace);
var json2 = jQuery.parseJSON(json.responseText);
console.log(json2._links.applications.href);
window.setTimeout(function(){registerApplication()}, 5000);
break;
case 3:
console.log(message);
break;
case 4:
break;
}
};
window.addEventListener('message', this.receiveMessage, false);
$(window).load(function() {
getUserResourceAuthRequest();
//console.log(access_token);
});
</script>
</body>
</html>
When I run this code: The last ajax query returns the error 409: Conflict, when it should be returning 201: Created
This is what my browser (Google Chrome) outputs:
The 401: Unauthorized error is supposed to happen, but the 409 Conflict, should not happen. So here is my question:
Can anyone spot why I get this 409 error instead of the 201 I should be getting?
The example code from Microsoft seems to work fine, but I want to avoid using that as it will take me a very long time to familiarize myself with it.
If there is missing data you need to spot the issue: Let me know in the comments, and i'll provide it!

If you replace
data: {'userAgent': 'InfoDisp1', 'endpointId' : '2d9dc28d-4673-4035-825c-feb64be28e4e', 'culture': 'en-US'}
with a string of that data instead I.E.
data: "{'userAgent': 'InfoDisp1', 'endpointId' : '2d9dc28d-4673-4035-825c-feb64be28e4e', 'culture': 'en-US'}"
it seems that data expects a string and in your example you are passing it a JSON object. Doing that makes your example work for me.

The problem seems to be with your static endpointId.
In their original helper libraries they have a method called generateUUID() which is in GeneralHelper. The best idea would be to use that method, however, if you feel like creating ayour own, go for it. The main point is that each of your application must have different endpointId.

Are you omitting the autodiscovery process for brevity only, or are you really skipping the autodiscovery in your code and assuming the URI where to post the 'create application'?
It seems more the second case to me, and this isn't right:
the URI where to create the application needs to be retrieved from the response of the user resource request (within getUserResource in the code you posted).
You have a link called applications there; its value contains the correct URI where to create the application.
http://ucwa.lync.com/documentation/KeyTasks-CreateApplication
P.S. I post here as well about the endpointId, seen I can't comment above
It is allowed to use the same application's endpointId on different clients. It is absolutely not to be assumed anyway that applications on different clients using the same endpointId will result in the same base application resource URI

I was getting this same problem using curl to experiment with this API, and failed at this same point until I figured out that in that case I needed to set content-type header to json:
curl -v --data "{'userAgent': 'test', 'endpointId': '54321', 'culture':'en-US', 'instanceID':'testinstance'}" --header 'Content-Type: application/json' --header 'Authorization: Bearer cwt=AAEBHAEFAAAAAAA..' 'https://lyncserver.com/ucwa/oauth/v1/applications'
That did the trick!

Related

Error in HTTP GET Request with gadgets.io.makeRequest

Sorry but I spent a half a day practicing first with gadgets.io.makeRequest, and can not understand why the request response contains an error. The code is Javascript working as OpenSocial gadget:
requestURI = "https://jazz.server.com:9443/rm/views?projectURL=https%3A%2F%2Fjazz.server.com%3A9443%2Frm%2Fprocess%2Fproject-areas%2F_FvrWIG3nEeexYJvvGxVsZg&oslc.query=true&oslc.prefix=rt=<https://jazz.server.com:9443/rm/types/>&oslc.select=rt:_W0SGoW3nEeexYJvvGxVsZg";
makeGETRequest(requestURI);
...
function makeGETRequest(url) {
try {
var params = {};
params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.GET;
params[gadgets.io.RequestParameters.HEADERS] = {
"Accept" : "application/rdf+xml",
"OSLC-Core-Version": "2.0"
}
gadgets.io.makeRequest(url, function(obj) {
console.log("===== HTTP REQUEST START =====");
console.log("Method : GET");
console.log("URL : " + url);
console.log("Response : " + obj.text);
console.log("====== HTTP REQUEST END ======");
}, params);
}
catch(err) {
console.log("Can not perform HTTP request because of error: " + err.message);
}
};
When I do the same request with REST Client in Firefox, everything works properly. But if I do that with the code above, then I get an error in the log (abbreviated):
===== HTTP REQUEST START =====
common.js:311 Method : GET
common.js:312 URL : https://jazz.server.com:9443/rm/views?projectURL=https%3A%2F%2Fjazz.server.…roject-areas%2F_FvrWIG3nEeexYJvvGxVsZg&oslc.query=true&oslc.prefix=rt=<https://jazz.server.com:9443/rm/types/>&oslc.select=rt:_W0SGoW3nEeexYJvvGxVsZg
common.js:313 Response : {"errorMessage":"Illegal character in query at index 178: https://jazz.server.com:9443/rm/views?projectURL=https%3A%2F%2Fjazz.server.com%3A9443%2Frm%2Fprocess%2Fproject-areas%2F_FvrWIG3nEeexYJvvGxVsZg&amp;oslc.query=true&oslc.prefix=rt=<https://jazz.server.com:9443/rm/types/>&oslc.select=rt:_W0SGoW3nEeexYJvvGxVsZg","errorClass":"java.lang.IllegalArgumentException","errorTrace":["java.net.URI.create(URI.java:871)","org.apache.http.client.methods.HttpGet.<init>
...
common.js:314 ====== HTTP REQUEST END ======
I tried to replace greater and less symbols by their hex values but there's no result. And there's no ideas currently.
May be somebody could make a fresh sight to the code and define the problem on the fly. Help me please, I'm at a dead end.
Thank you very much in advance for any advice!
The error in your Response indicates the Java system on the server side can't create a valid URI from your query. Therefor it throws back an error
My best guess would be the dot just before query=true in oslc.query=true. And therefor all following uses of oslcDOT .
From RFC 1738 specification:
Thus, only alphanumerics, the special characters "$-_.+!*'(),", and reserved characters used for their reserved purposes may be used unencoded within a URL.
I discovered that gadgets.io.makeRequest isn't very stable as I would like to expect. May be I do some wrong but sometimes this function completes without any feedback and without starting the response function in the parameters. I changed to next code:
function makeGETRequest(urlValue) {
try {
$.ajax({
url: urlValue,
type: 'GET',
dataType: 'text',
headers: {
'Accept': 'application/rdf+xml',
'OSLC-Core-Version': '2.0'
},
success: function (result) {
var data = result;
},
error: function (error) {
console.log("Can not perform HTTP request because of error: " + error.message);
}
});
}
catch(err) {
console.log("Can not perform HTTP request because of error: " + err.message);
}
};
And there's no problem!

How to send an HTTP request with a header parameter?

I'm very new to javascript and web programming in general and I need some help with this. I have an HTTP request that I need to send through javascript and get need to store the output in a variable. I tried using just the call url:
https://api.fantasydata.net/nfl/v2/JSON/PlayerSeasonStats/2015
But it returns an authentication error because I didn't send my API key and it doesn't show me how to do it just in the URL. The API key is listed as a header and not a paramater and I'm not sure what to do with that. I tried using the XMLHttpRequest() class but I'm not quite sure I understand exactly what it does nor could I get it to work.
The actual HTTP Request
GET https://api.fantasydata.net/nfl/v2/JSON/PlayerSeasonStats/2015 HTTP/1.1
Host: api.fantasydata.net
Ocp-Apim-Subscription-Key: ••••••••••••••••••••••••••••••••
I just need to figure out how to send that request along with the key and how to store the JSON doc it returns as a variable in javascript.
EDIT: This is what I have so far:
function testingAPI(){
var key = "8a1c6a354c884c658ff29a8636fd7c18";
httpGet("https://api.fantasydata.net/nfl/v2/JSON/PlayerSeasonStats/2015",key );
alert(xmlHttp.responseText);
var x = 0;
}
function httpGet(theUrl,key)
{
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", theUrl, false ); // false for synchronous request
xmlHttp.setRequestHeader("Ocp-Apim-Subscription-Key",key);
xmlHttp.send( null );
return xmlHttp.responseText;
}
Thank you!
If it says the API key is listed as a header, more than likely you need to set it in the headers option of your http request. Normally something like this :
headers: {'Authorization': '[your API key]'}
Here is an example from another Question
$http({method: 'GET', url: '[the-target-url]', headers: {
'Authorization': '[your-api-key]'}
});
Edit : Just saw you wanted to store the response in a variable. In this case I would probably just use AJAX. Something like this :
$.ajax({
type : "GET",
url : "[the-target-url]",
beforeSend: function(xhr){xhr.setRequestHeader('Authorization', '[your-api-key]');},
success : function(result) {
//set your variable to the result
},
error : function(result) {
//handle the error
}
});
I got this from this question and I'm at work so I can't test it at the moment but looks solid
Edit 2: Pretty sure you should be able to use this line :
headers: {'Authorization': '[your API key]'},
instead of the beforeSend line in the first edit. This may be simpler for you
With your own Code and a Slight Change withou jQuery,
function testingAPI(){
var key = "8a1c6a354c884c658ff29a8636fd7c18";
var url = "https://api.fantasydata.net/nfl/v2/JSON/PlayerSeasonStats/2015";
console.log(httpGet(url,key));
}
function httpGet(url,key){
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", url, false );
xmlHttp.setRequestHeader("Ocp-Apim-Subscription-Key",key);
xmlHttp.send(null);
return xmlHttp.responseText;
}
Thank You
**`
U need to have a
send();
Statement.
That way you can send the request to the site server.
`**

jQuery's .ajaxSend: can't add data dynamically

I use $(document).ajaxSend(...) to dynamically add data (POST args) to some Ajax requests, when necessary.
Adding data works when some datas have been defined in an $.ajax(...) call. But if no data has been defined in the $.ajax setting, my $.ajaxSend can't add data to the settings.
Here is my $.ajaxSend interceptor:
$(document).ajaxSend(function(e, request, settings) {
if(settings.csrfIntention) {
var requestToken = {
intention: settings.csrfIntention,
hash: self.tokens[settings.csrfIntention]
}
if(!settings.data) {
settings.data = '_token=' + encodeURIComponent(JSON.stringify(requestToken));
}
else if(typeof(settings.data) == 'string') {
settings.data += '&_token=' + encodeURIComponent(JSON.stringify(requestToken));
}
else if(!settings.data._token) {
settings.data._token = requestToken;
}
}
});
And an example of $.ajax call that works:
$.ajax({
url: opts.close.url,
method: 'POST',
data: { foo:'bar' },
csrfIntention: 'ajax_close_ticket',
success: function(data) { ... }
});
The $.ajaxSend works and settings.data is set to:
foo=bar&_token=%7B%22intention%22%3A%22ajax_close_ticket%22%2C%22hash%22%3A%22uXV1AeZwm-bZL3KlYER-Dowzzd1QmCmaT6aJFjWLpLY%22%7D
Serverside, I can retrieve the two fields: foo and _token.
Now, if I remove the data object in the $.ajax call, the output of $.ajaxSend seems Ok, too:
_token=%7B%22intention%22%3A%22ajax_close_ticket%22%2C%22hash%22%3A%225cK2WIegwI6u8K_FrxywuauWOo79xvhIcASQrZ9QPZQ%22%7D
Yet, the server don't receive my _token field :(
Another interesting fact: when I have the two fields, the Chromium dev tools under the Network tab displays the two fields under a "Form Data" title. When _token is alone, "Form Data" is replaced by "Request Payload".
Edit: just understood why _token is not interpreted by the server. If no data has been previously set in $.ajax call, jQuery does not add the right Content-Type in HTTP headers, it set it to text/plain instead of application/x-www-form-urlencoded. How can I force jQuery to add this header?
Edit 2: Solution found. Will post an answer to help other people...
I apologize for my bad English
I believe that the solution to his case would be the use of the function "ajaxSetup"
var requestToken = {
intention: settings.csrfIntention,
hash: self.tokens[settings.csrfIntention]
};
$.ajaxSetup({
data : {
_token: requestToken
}
});
this way all your orders took the data as the desire
A simpler approach:
var requestToken = {
intention: settings.csrfIntention,
hash: self.tokens[settings.csrfIntention]
}
var myForm = {};
myForm['_token'] = encodeURIComponent(JSON.stringify(requestToken));
In your ajax call:
myForm['foo'] = 'bar';
$.ajax({
url: opts.close.url,
method: 'POST',
data: myForm,
csrfIntention: 'ajax_close_ticket',
success: function(data) { ... }
});
//This sends foo and _token
Finally I answer my question after only 10 minutes...
In HTTP, the form arguments are sent into the request body, under the headers. The header Content-Type should be present to declare the data type. When this header is set to application/x-www-form-urlencoded, the server will understand that you sent a form and will parse the request body as a GET URL (you know, the foo=bar&key=val format).
jQuery adds this header automatically when you set a data object into the $.ajax call. Then it passes the request to the $.ajaxSend callback, which adds its proper fields.
When no data has been provided in request settings, jQuery do not add an unnecessary Content-Type: x-www-form-urlencoded in the request headers. Then, when you append the request body into your $.ajaxSend callback, jQuery do not check the data again and declares the content as text/plain. The server has nothing to do with text/plain so it does not interpret the body data as form fields.
You can obviously force jQuery to change the header to application/x-www-form-urlencoded, let's take the code of my original post :
if(!settings.data) {
settings.data = '_token=' + encodeURIComponent(JSON.stringify(requestToken));
}
Now add the right header:
if(!settings.data) {
settings.data = '_token=' + encodeURIComponent(JSON.stringify(requestToken));
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
}

Send HEAD request in Google App Script

I am developing a Google App Script to determine the size of a remote resource without downloading it. The code is as follows
function getRemoteFileSize()
{
var params = { "method" : "head" };
var resp = UrlFetchApp.fetch("https://www.google.com/images/srpr/logo11w.png", params);
Logger.log("Remote File Size: " + resp.getAllHeaders()["Content-Length"]);
}
However, Google App Script does not seem to support head requests and the code above cannot be executed.
What can be a viable alternative other than issuing a GET request ?
I am open to all suggestions including usage of a third-party service which has an API
You can try to override the method by setting the "headers" advanced parameter:
var params = {"method" : "GET", "headers": {"X-HTTP-Method-Override": "HEAD"}};
I've never used "HEAD", so I can't tell you for sure that this will work, but I have used the method override for "PATCH", and had that work.
I found that for my circumstance, I was able to use the Range header to request 0 bytes and then inspect the response headers which held the file size:
var params = {
method: "GET",
headers: {
Range: "bytes=0-0",
},
};
var response = UrlFetchApp.fetch(fileUrl,params);
var headers = response.getHeaders();
var fileSizeString = headers['Content-Range']
var fileSize = +headers['Content-Range'].split("/")[1];
The response headers had Content-Range=bytes 0-0/139046553 as a value that I could then use to convert to an integer (139046553) number of bytes.

ASP.NET MVC JsonResult return 500

I have this controller method:
public JsonResult List(int number) {
var list = new Dictionary <int, string> ();
list.Add(1, "one");
list.Add(2, "two");
list.Add(3, "three");
var q = (from h in list where h.Key == number select new {
key = h.Key,
value = h.Value
});
return Json(list);
}
On the client side, have this jQuery script:
$("#radio1").click(function() {
$.ajax({
url: "/Home/List",
dataType: "json",
data: {
number: '1'
},
success: function(data) {
alert(data)
},
error: function(xhr) {
alert(xhr.status)
}
});
});
I always get an error code 500. What's the problem?
Thank you
If you saw the actual response, it would probably say
This request has been blocked because
sensitive information could be
disclosed to third party web sites
when this is used in a GET request. To
allow GET requests, set
JsonRequestBehavior to AllowGet.
You'll need to use the overloaded Json constructor to include a JsonRequestBehavior of JsonRequestBehavior.AllowGet such as:
return Json(list, JsonRequestBehavior.AllowGet);
Here's how it looks in your example code (note this also changes your ints to strings or else you'd get another error).
public JsonResult List(int number) {
var list = new Dictionary<string, string>();
list.Add("1", "one");
list.Add("2", "two");
list.Add("3", "three");
var q = (from h in list
where h.Key == number.ToString()
select new {
key = h.Key,
value = h.Value
});
return Json(list, JsonRequestBehavior.AllowGet);
}
While JustinStolle's answer solves your problem, I would pay attention to the error provided from the framework. Unless you have a good reason to want to send your data with the GET method, you should aim to send it with the POST method.
The thing is, when you use the GET method, your parameters gets added to your request url instead of added to the headers/body of your request. This might seem like a tiny difference, but the error hints why it's important. Proxy servers and other potential servers between the sender and the receiver are prone to logging the request url and often ignore the headers and/or body of the request. This information is also often regarded as non important/secret so any data exposed in the url is much less secure by default.
The best practice is then to send your data with the POST method so your data is added to the body instead of the url. Luckily this is easily changed, especially since you're using jquery. You can either use the $.post wrapper or add type: "POST" to your parameters:
$.ajax({
url: "/Home/List",
type: "POST",
dataType: "json",
data: { number: '1' },
success: function (data) { alert(data) },
error: function (xhr) { alert(xhr.status) }
});

Categories