Authentication through JavaScript for REST api consumption
Hi,
I am trying to write some JavaScript that consumes some of my Jazz services, but does so outside the context of a Jazz web application. Take this snippet as an example:
I am using the Firebug JavaScript console as a quick & dirty test bed. When I run this snippet in the context of an authenticated session login to our web app it works fine, as expected. When I execute it from a different context it fails as expected due to authorization.
My question is how can I authenticate via JavaScript from outside the context of a proper Jazz web app so that other web applications can consume my service? I assume I need to precede my GET with a POST to an auth service but am unsure of both the service and syntax.
Any help is greatly appreciated. Thanks!
I am trying to write some JavaScript that consumes some of my Jazz services, but does so outside the context of a Jazz web application. Take this snippet as an example:
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", "https://localhost:9443/jazz/secure/service/com.ibm.assetservice/myAsset", false);
xmlhttp.send(null);
I am using the Firebug JavaScript console as a quick & dirty test bed. When I run this snippet in the context of an authenticated session login to our web app it works fine, as expected. When I execute it from a different context it fails as expected due to authorization.
My question is how can I authenticate via JavaScript from outside the context of a proper Jazz web app so that other web applications can consume my service? I assume I need to precede my GET with a POST to an auth service but am unsure of both the service and syntax.
Any help is greatly appreciated. Thanks!
6 answers
I thought I was on the right track with this:
No luck yet. I get an exception: "Permission denied to call method XMLHttpRequest.open"
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", "https://localhost:9443/jazz/secure/web/console/j_security_check?j_password=ADMIN&j_username=ADMIN", false);
xmlhttp.send(null);
No luck yet. I get an exception: "Permission denied to call method XMLHttpRequest.open"
Hi John, without understanding more about your environment it's hard to diagnose what's wrong, but I suspect you might be blocked by the browser's "same origin policy" (http://en.wikipedia.org/wiki/Same_origin_policy) which prevents you from making XmlHttpRequests (XHR) to sites other than the site that served you the web page containing the script.
For instance, if your page is:
http://foo.com:80/
... XHR requests to any of the following web sites would fail because of the same origin policy:
http://blah.com/ (different host)
https://foo.com:443/ (different port)
In your case, if your page is served by anything other than https://localhost:9443/, the same origin policy will block it.
One other word of warning. The custom authentication protocol we use in Team Concert 1.0 will likely change with the next major release, so if you write any code with this protocol in mind, I'd advise you to encapsulate it in an API of your own so that when our protocol changes you only need to update one code block.
Bill Higgins
Jazz Foundation Web UI Team
For instance, if your page is:
http://foo.com:80/
... XHR requests to any of the following web sites would fail because of the same origin policy:
http://blah.com/ (different host)
https://foo.com:443/ (different port)
In your case, if your page is served by anything other than https://localhost:9443/, the same origin policy will block it.
One other word of warning. The custom authentication protocol we use in Team Concert 1.0 will likely change with the next major release, so if you write any code with this protocol in mind, I'd advise you to encapsulate it in an API of your own so that when our protocol changes you only need to update one code block.
Bill Higgins
Jazz Foundation Web UI Team
I forgot to mention that there are several solutions to working around the same origin policy if you need to do cross domain XHR requests. Of course if you decide to work around the same origin policy you should only do after considering security implications, since the same origin policy was created to prevent various attacks.
A popular solution to getting around the same origin policy is to deploy a proxy service. I.e. on the server that serves your web page, create a script or servlet or whatever that passes through requests to remote servers. If you do this, you should institute a whitelist so only servers you've deemed as safe may be called.
There are other solutions which you can read about on the web. Try Googling on "same origin cross domain" for some pointers.
Bill Higgins
Jazz Foundation Web UI Team
A popular solution to getting around the same origin policy is to deploy a proxy service. I.e. on the server that serves your web page, create a script or servlet or whatever that passes through requests to remote servers. If you do this, you should institute a whitelist so only servers you've deemed as safe may be called.
There are other solutions which you can read about on the web. Try Googling on "same origin cross domain" for some pointers.
Bill Higgins
Jazz Foundation Web UI Team
Thanks Bill. You're right; I originally miscategorized the problem and later discovered it was a cross-domain scripting issue.
The solution I ultimately went with is script injection, enabling me to invoke the REST API via a dynamically created SCRIPT tag where the src of the tag is my REST API to invoke. This works well and seems to be a common solution among "mash-up" apps. I then ran into the next problem with this approach, however, which is that our REST API returns XML for detailed asset views and not JSON or JS, meaning the call is made, the XML is returned, but then eval'ed. You can not get access to the SCRIPT tag's text content (to pull the XML from after the call) which I assume is another security measure. One solution to this problem is being employed in the Yahoo API, where a callback name is passed in to the API via a CGI param. You implement the callback on the client and Yahoo simply wraps the returned data into a callback function that you specify. Simple and elegant. I will be trying to see if we can adopt a similar pattern in our API.
Typically being a server dev, I was actually just trying to use our REST API in the context of a mash-up for demo purposes. I had decided to host my mash-up via a Google Gadget, which could then be hosted in a Jazz Dashboard widget, other dashboards (orkut, igoogle, etc), or any arbitrary web page. The gadget will be hosted on a google server, hence the cross-domain script problem.
I address that a proxy is the "right" way to approach this problem, but I feel like dynamic script tags (or "wrappers" like provided in beta form for Gadget API) is the everyman's approach needed for adoption and use of these APIs in a more wide-spread manner. You would not see a proliferation of data mash-ups if a pre-req was custom server code. Maybe Jazz could provide a configurable proxy out of the box to better facilitate this approach for consumption of remote services?
The solution I ultimately went with is script injection, enabling me to invoke the REST API via a dynamically created SCRIPT tag where the src of the tag is my REST API to invoke. This works well and seems to be a common solution among "mash-up" apps. I then ran into the next problem with this approach, however, which is that our REST API returns XML for detailed asset views and not JSON or JS, meaning the call is made, the XML is returned, but then eval'ed. You can not get access to the SCRIPT tag's text content (to pull the XML from after the call) which I assume is another security measure. One solution to this problem is being employed in the Yahoo API, where a callback name is passed in to the API via a CGI param. You implement the callback on the client and Yahoo simply wraps the returned data into a callback function that you specify. Simple and elegant. I will be trying to see if we can adopt a similar pattern in our API.
Typically being a server dev, I was actually just trying to use our REST API in the context of a mash-up for demo purposes. I had decided to host my mash-up via a Google Gadget, which could then be hosted in a Jazz Dashboard widget, other dashboards (orkut, igoogle, etc), or any arbitrary web page. The gadget will be hosted on a google server, hence the cross-domain script problem.
I address that a proxy is the "right" way to approach this problem, but I feel like dynamic script tags (or "wrappers" like provided in beta form for Gadget API) is the everyman's approach needed for adoption and use of these APIs in a more wide-spread manner. You would not see a proliferation of data mash-ups if a pre-req was custom server code. Maybe Jazz could provide a configurable proxy out of the box to better facilitate this approach for consumption of remote services?
Hi John, indeed an out of the box proxy is something we intend to include with our June 2009 release. See Enhancement 60153
- Bill Higgins
Jazz Foundation Web UI Team
- Bill Higgins
Jazz Foundation Web UI Team
Hi John,
for 1.0.1, we included a solution to enable cross domain requests, based
on dojo's iframe proxy support.
see
com.ibm.team.repository.web/resources/transport/ExternalTeamServerClient.js
and ExternalTeamServerDescription.js. This allows you to connect to an
different Jazz server, handles the authentication etc. However, you have
to enable cross domain XHR support on the target server and whitelist
the source server. see the
com.ibm.team.repository.service.xdomain.enabled and ...whitelist
configuration properties.
also note that the services, which are allowed to be invoked by cross
domain requests, are whitelisted as well. see
com.ibm.team.repository.service/resources/xip_server.html, where the
service whitelist is hard-coded (for now).
--
MikeS
Jazz Agile Planning team
for 1.0.1, we included a solution to enable cross domain requests, based
on dojo's iframe proxy support.
see
com.ibm.team.repository.web/resources/transport/ExternalTeamServerClient.js
and ExternalTeamServerDescription.js. This allows you to connect to an
different Jazz server, handles the authentication etc. However, you have
to enable cross domain XHR support on the target server and whitelist
the source server. see the
com.ibm.team.repository.service.xdomain.enabled and ...whitelist
configuration properties.
also note that the services, which are allowed to be invoked by cross
domain requests, are whitelisted as well. see
com.ibm.team.repository.service/resources/xip_server.html, where the
service whitelist is hard-coded (for now).
--
MikeS
Jazz Agile Planning team