View Full Version : Tonr2 and Spaklr2 do not use access tokens to retrieve photos
davidfoley
May 18th, 2011, 05:49 AM
Hi, having analyzed the tonr2 and sparklr2 apps, I can't see where access tokens fit in to how these apps work.
This is a simplified session recorded by Charles Web Debugging Proxy. The interaction is that I visit tonr, click to view photos, am redirected to sparklr where I authorize successfully, then redirected back to tonr, where is successfully retrieves the "marissa" users photos
http://localhost:8080/sparklr/oauth/user/authorize
http://localhost:8080/tonr/sparklr/photos.jsp?code=HeCXJE
http://localhost:8080/tonr/sparklr/photo/3
http://localhost:8080/tonr/sparklr/photo/1
http://localhost:8080/tonr/sparklr/photo/5
As you can see, the authorization code is returned to photos.jsp- but no access tokens are used to retrieve any of the photos!
Can someone explain? Do both of these apps need refactoring to support access to protected resources using OAuth2? Or is this a bug or limitation of the examples?
To clarify, I would have expected Sparklr to deny access to the photos without an appropriate OAuth access token. How can I accomplish this, in general terms?
Thanks
stoicflame
May 18th, 2011, 08:07 AM
Notice the photos are being returned from tonr, even though they reside at sparklr. In this case, tonr is "proxying" the request for the photos over to sparklr, adding the oauth token in the request header.
davidfoley
May 18th, 2011, 08:36 AM
Hi stoicflame- thanks for getting back to me. I had assumed that it was possible that proxy requests were communicating tokens in request headers, but Charles does not seem to pick them up. Regardless, I developed a standalone javascript app, that definitely DOES NOT use access tokens to retrieve the photos. Here is what Charles recorded. You can see near the end that I'm requesting photos directly from the localhost:8080/sparklr/rest/jpg/photo/ uri with not tokens in the request. N.B. I set up a virtual host on a local Apache Http Server called 'test.local'. I embedded the javascript client (below) into a html page and ran it from that location.
http://test.local/ 200 GET test.local
http://localhost:8080/sparklr/json/photos?callback=application.onLoadResourceManifest 302 GET
http://localhost:8080/sparklr/login.jsp;jsessionid=4EDC2013A400793A561C99CFDCE59 7AE 200 GET
http://localhost:8080/sparklr/oauth/user/authorize?client_id=tonr&redirect_uri=http://test.local/&response_type=code 302 GET
http://localhost:8080/sparklr/login.jsp;jsessionid=A9D8E0018EF3F83E3BD271409DECD B56 200
http://localhost:8080/sparklr/login.do 302 POST
http://localhost:8080/sparklr/oauth/user/authorize?client_id=tonr&redirect_uri=http://test.local/&response_type=code 302 GET
http://localhost:8080/sparklr/oauth/confirm_access 200 GET
http://localhost:8080/sparklr/oauth/user/authorize 302 POST
http://test.local/?code=i67LU0 200 GET test.local
http://localhost:8080/sparklr/json/photos?callback=application.onLoadResourceManifest 200 GET
http://localhost:8080/sparklr/rest/jpg/photo/1 200 GET
http://localhost:8080/sparklr/rest/jpg/photo/3 200 GET
http://localhost:8080/sparklr/rest/jpg/photo/5 200 GET
I'm not able to attach files for some reason, but here is the totality of the JavaScript client. The renderPhotos method (near end) is responsible for injecting the image tags into the DOM.
<script>
if ('function' != typeof Function.prototype.bind)
{
Function.prototype.bind= function (scope)
{
var func= this
, presetArgs= Array.prototype.slice.call(arguments, 1);
return function ()
{
return func.apply(scope, presetArgs.concat(arguments));
}
}
}
(
{
/**
* Initialize the application
*
* @private
* @param {boolean} [tryPhotoLoadWithoutAuth]
* @return {void}
*/
initialize: function (tryPhotoLoadWithoutAuth)
{
// export this object to the global scope
window.application= this;
if (this.isAuthenticated())
{
this.loadJsonResourceManifest();
}
else
{
this.setupAuthentication();
if (tryPhotoLoadWithoutAuth)
{
this.loadJsonResourceManifest();
}
}
}
/**
* Configuration settings
*
* @private
* @type {Object}
*/
, settings :
{
host : "http://localhost:8080/sparklr"
, authEndpoint : "/oauth/user/authorize"
, manifestUri: "/json/photos?callback=application.onLoadResourceManifest"
, resourceUri: "/rest/jpg/photo/"
, clientId : "tonr"
}
/**
* Get the sparklr resource manifest uri
* @return {string}
*/
, getJsonResourceManifestUri: function ()
{
return this.settings.host + this.settings.manifestUri;
}
/**
* Get the sparklr authentication endpoint
*
* @return {string}
*/
, getAuthUri : function()
{
return this.settings.host + this.settings.authEndpoint
}
/**
* Get the sparklr OAuth client id (aka consumer id)
* @return {string}
*/
, getClientId : function()
{
return this.settings.clientId;
}
/**
* Get the uri to which Sparklr should redirect to after
* authentication has completed. At the moment, its just
* this page
*
* @return {string}
*/
, getRedirectUri : function()
{
return window.location;
}
/**
* Try and locate an oauth security token in the applications
* uri, if it exists. If its not incuded in the url, the method
* returns null
*
* return {string|null}
*/
, findOAuthToken: function ()
{
if (this.oauthToken)
return this.oauthToken;
var match = location.search.match(/code=(\w+)/);
if (null != match)
this.oauthToken= match[1];
return this.oauthToken || null;
}
/**
* Determine if the application has been authenticated.
*
* @private
* @return {boolean}
*/
, isAuthenticated: function ()
{
return null != this.findOAuthToken();
}
/**
* Authenticate with Sparklr. Authentication is accomplished
* by rendering a link to Sparllrs authentication uri
*
* @private
* @return {void}
*/
, setupAuthentication: function ()
{
console.log("The client has not yet authenticated");
var authUri = this.getAuthUri() + "?client_id="
+ this.getClientId() + "&redirect_uri="
+ this.getRedirectUri() + "&response_type=code"
this.renderConnectButton(authUri);
}
/**
* Load the manifest detailing the photos available to retrieve from Sparklr
*
* @private
* @return {void}
*/
, loadJsonResourceManifest: function ()
{
var script= document.createElement('script');
script.src=this.getJsonResourceManifestUri();
script.onerror= this.onLoadResourceManifestFault.bind(this);
document.body.appendChild(script);
console.info("requesting resource manifest from " + script.src);
}
/**
* Calculate the sparklr uri from which to retrieve the protected photo
* @return {string}
*/
, calculatePhotoUri: function (photoId)
{
return this.settings.host + this.settings.resourceUri + photoId;
}
/**
* The resource manifest has loadewd
*
* @private
* @return {void}
*/
, onLoadResourceManifest: function (manifest)
{
this.renderPhotos(manifest.photos);
}
/**
* Render each photo as an image element
*
* @private
* @return {void}
*/
, renderPhotos: function (photoList)
{
photoList.forEach
(
function (photo)
{
var image= document.createElement('image')
image.src= this.calculatePhotoUri(photo.id);
document.body.appendChild(image);
console.info("added photo " + image.src)
}
, this
);
}
/**
* Render the connect button.
*
* @private
* @return {void}
*/
, renderConnectButton: function (uri)
{
$('<a href="'+uri+'">Connect to Sparklr</a>').appendTo(document.body);
}
/**
* Loading of the resource manifest from Sparklr failed.
*
* @private
* @param {Event} errorEvent
* @return {void}
*/
, onLoadResourceManifestFault: function (errorEvent)
{
console.dir(errorEvent);
throw new Error("Could not load resource manifest from " + this.getJsonResourceManifestUri());
}
}
)
.initialize(/*true*/);
</script>
This is very unusual. Is it possible that as no access token was provided, that the resources were not protected by the OAuth2ProtectedResourceFilter, and that the security filter chain cascaded, then recognising that the client had authenticated via http basic at the sparklr site, so allowing access? I confess, I don't understand.
stoicflame
May 18th, 2011, 10:00 AM
I'm not sure exactly what you're expecting.
Charles wouldn't pick up the token authentication because it's not being exposed in any way to the browser client... it's all back-end.
When I bring up sparklr and hit http://localhost:8080/sparklr/rest/jpg/photo/1 I get an error, indicating that it is locked down.
When I log in (establishing a session) and hit http://localhost:8080/sparklr/rest/jpg/photo/1 I get the photo. I don't need an access token because I have an authenticated session and the sparklr app is serving me up the picture directly. I only need an access token when I go through an oauth "client" (tonr, in this case).
Everything seems to be working as designed for me.
davidfoley
May 18th, 2011, 10:29 AM
Hey,
When I bring up sparklr and hit http://localhost:8080/sparklr/rest/jpg/photo/1 I get an error, indicating that it is locked down.
When I log in (establishing a session) and hit http://localhost:8080/sparklr/rest/jpg/photo/1 I get the photo. I don't need an access token because I have an authenticated session and the sparklr app is serving me up the picture directly. I only need an access token when I go through an oauth "client" (tonr, in this case).
Thats the behaviour I'm getting, and I understand why. I suppose what I was expecting, was that even though I had logged in to localhost:8080/sparklr and an authenticated session had been created, that I would not have been able to successfully make a cross domain request (from test.local) for the photos, whether I had logged in to sparklr or not- or am I missing something (and I well could be!), because thats what the javascript client does... BTW- I realise that the session id is communicated in the COOKIE header making requests for the photos as a result of the having an authenticated session, its just that I somehow expected that requests for OAuth2 protected resources would only ever be transferred subject to OAuth2 security measures, regardless of the session authentication.
stoicflame
May 21st, 2011, 01:16 PM
I honestly don't know how your javascript client is getting the photos information cross-domain. I thought the same-origin policy prevented that.
You actually can lock down resources to require an authenticated oauth access token, making them impossible to access even via authenticated session. It's just the the sparklr example doesn't do it that way.
davidfoley
May 23rd, 2011, 07:07 AM
The same-origin policy is limited to executable JavaScript at runtime, and is a protection measure that prevents direct communication between scripts loaded from different domains executed in the same page (e.g. the window object in an iFrame cannot access its parents DOM).
Scripts, images, css, can all be loaded x-domain. One common abuse of this is hot-linking (pulling in assets hosted on other domains). Same-origin policy does not apply. Happen to know (in general) how i would be able to modify sparklr to prevent cross domain access to resources unless authenticated via OAuth?
Thanks!
stoicflame
May 24th, 2011, 11:05 PM
It's basically about providing an interceptor or a filter that will look at the security context and throw access denied unless the authentication is an OAuth authentication.
This will be a bit easier in 1.0.0.M4 because you'll be able to use special expressions in @PreAuthorize annotation or config file that will allow you to deny access except to oauth-authenticated requests.
davidfoley
May 25th, 2011, 04:46 AM
Thanks for getting back to me- looking forward to 1.0.0.M4 :)
Powered by vBulletin® Version 4.2.1 Copyright © 2013 vBulletin Solutions, Inc. All rights reserved.