14

I want to create a web widget that will display information from my site.

The widget will be included in the client's website HTML using JavaScript, and should only be usable for my clients -- web sites that were registered at my site.

The information in the widget should be specific to the user who is currently visiting the client's site.

So, I need to authenticate both the client (website owner) and the resource owner (website visitor). This seems to map nicely to OAuth 2.0, but I couldn't find a complete example or explanation for such an implementation.

Any resources or pointers to such information will be appreciated.

Update: I've stumbled upon this article, which provides an outline for an approach that uses OAuth. However, it is not detailed enough for me to really understand how to use this with OAuth 2.

davidrac
  • 10,723
  • 3
  • 39
  • 71
  • *"web sites that were registered at my site"* — Are you assuming you can use OAuth for your client's authentication? Or only for the visitor? And how are you creating that widget? (JavaScript, or something on the client's server?) – Arjan Nov 04 '12 at 14:22
  • This is my requirement. I need to authenticate both the client and the resource owner. I assumed I'll need to use OAuth 2.0 implicit flow and compare the registered callback URL to the URL in the call. However, this is part of the explanation I'm looking for... – davidrac Nov 04 '12 at 14:26
  • So, given you're using the word "web widget" and your [earlier post](http://stackoverflow.com/questions/12998701/how-to-control-who-uses-my-web-widget): some JavaScript include in the HTML? (Hence: any client token would be visible in the HTML source code, and could be abused by anyone who wants to include the widget in their site? I am no expert though.) – Arjan Nov 04 '12 at 16:35
  • Yes. Javascript included in the HTML. Of course, no client secret should be involved. This is the purpose of the implicit flow of OAuth 2.0. I am no expert either and this is why I'm asking this question. – davidrac Nov 04 '12 at 17:26
  • I've stumbled upon this article [http://supercollider.dk/2009/01/oauth-and-client-side-widgets-154], which provides an outline for an approach that uses OAuth. However, it is not detailed enough for me to really understand how to use this with OAuth 2. – davidrac Nov 08 '12 at 09:25
  • That article ([fixed link](http://supercollider.dk/2009/01/oauth-and-client-side-widgets-154)) is a nice find! Its *"To make sure the Consumer executed the JavaScript file, the Service Provider can save a Verification Token in a browser cookie as the file is requested. When the authorization page is loaded, the Service Provider can verify that the specified Request Token corresponds with the Verification Token in the cookie."* seems solid to me. (Assuming your users are smart enough to only enter their credentials on your site.) Any specifics that cause you trouble? – Arjan Nov 08 '12 at 19:33
  • I thought I posted this before, but apparently not: see also [Stack Exchange JS script](http://api.stackexchange.com/docs/js-lib) and [the generic documentation](http://api.stackexchange.com/docs/authentication) for some ideas. – Arjan Nov 22 '12 at 22:41

1 Answers1

9

There are many large organizations that have done this, and I'm sad to see no other answers for this question since it's such an important web pattern.

I'm going to presume that you are not rolling your own OAuth 2.0 provider from scratch, if you are - well done otherwise you should be using something kickass like Doorkeeper to do this for you.

Now, in OAuth 2.0 you have the following entities:

  1. Users registered on your website
  2. Applications registered on your website (who subscribe to your oauth2)
  3. User Permissions which is a list of Applications that a user has 'allowed'
  4. Developer (who is consuming your auth API / widgets and building an Application)

The first thing to note is you must have a domain name associated with each Application. So if a developer registers for a API token / secret on your website, the Application he creates is mapped to a unique domain.

Now, I presume that the flow for an application to authenticate users via your website is already clear. That being said, you don't need to do much for this to work.

When an Application sends the user to your website (in order to sign in) you place a session cookie on the user's computer. Lets call this "Cookie-X".

Now the user is authenticated by your website and goes back to the Application. There we want to show a custom widget with information pertaining to that user.

The developer will be need to copy paste some code into this app.

The flow is like this:

  1. The code will contain a url to your website with his Application ID (not secret) which he got when registering his application on your website.

  2. When that code runs, it will ping your website with his appId. You need to check that AppID with your database, and additionally check that the referrer url is from the same domain as that which is registered in your website for that AppID. Edit: Alternatively or additionally, the code can check for document.domain and include it in the ping to your website, allowing you to verify that the request has come from the domain that has registered with the given AppID.

  3. If that is correct, you reply back with some JS code.

  4. Your JS code looks for the session cookie your website had set when the user had signed in. If that cookie is found, it pings back to your website with the session and your website responds with the custom view content.

Edit: as rightfully mentioned in a comment, the cookie should be HttpOnly to safeguard against common XSS attacks.

Additional Notes

The reasons this is a secure approach:

  1. The AppId and domain name are a good enough combination to verify that other people are not fetching this information. Even thou the appId is visible in the applications html source, the domain name would have to be spoofed by anyone attempting to use someone else's AppID.

  2. Presuming someone takes an AppID which is not his, and writes code to spoof the domain name of the referrer when requesting for your widget, he still won't be able to see any information. Since you are showing user specific information, the widget will only render if your website can find the session cookie it placed on the users browser which can't really be spoofed. There are ways around like session-hijacking, etc. But I think that's beyond the scope of this question.

Other Methods Just by looking at Facebook's Social Plugins, you can tell that there are other options.

For example, one might be to use an Iframe. If you ask the developer to add an Iframe to his application, you can even reduce a few of the steps mentioned above. But you will have to add JS along with it (outside the iframe) to grab the correct domain, etc. And ofcourse from an accessibility and interface standpoint I'm not very found of Iframes.

vvohra87
  • 5,594
  • 4
  • 22
  • 34
  • *"the referrer url"* -- [The article](http://supercollider.dk/2009/01/oauth-and-client-side-widgets-154) the OP found uses `if(document.domain=='the-registered-domain.com')` Any reason why you rely on REFERER instead? (It might be empty.) *"Your JS code looks for the session cookie"* -- this needs that cookie [to be NOT HttpOnly](http://www.codinghorror.com/blog/2008/08/protecting-your-cookies-httponly.html). But also, the JavaScript would be running on the developer's domain, and cannot access the session cookie then? Aren't steps 4 and 5 just one? – Arjan Nov 21 '12 at 14:59
  • @Arjan thanks for pointing out httponly - it didn't strike me at the time. I've added that. document.domain vs referrer is not a debate imho. I've added both. I would not want to do the check on the client side regardless, so the js can send over the `document.domain` value and on the server we can check both for legitimacy. Oh, also I merged 4 and 5... long answers are a little tedious in the SO editor. – vvohra87 Nov 21 '12 at 15:18
  • I wasn't referring to merging the text :-) Instead I meant: how can the JavaScript even access that cookie, as it originated from a different domain? I can understand this works when 4/5 is really a single step, like *"Your JS code pings the website, which makes the browser also send the "Cookie-X" that originated from your domain. Your server checks the cookie and responds with the custom view content."* But not from JavaScript code? Also, I feel `if(document.domain== ...)` is one of the most important details in that linked article; without that the code would succeed on *any* website? – Arjan Nov 21 '12 at 19:09
  • Like: in step 1, 2 and 3 you don't check for the cookie. So, this could be server side code, with a fake REFERER. In step 4, you don't check for the `if(document.domain== ...)`. So even though receiving the cookie indicates that it was an actual browser that sent the request, you cannot be sure it was also the browser that did the first 3 steps? (I feel those are exactly the flaws the linked article solved?) – Arjan Nov 21 '12 at 19:25
  • @Arjan Not at all! If you are using OAuth 2.0, you will need to send in an access token with each request regardless! I've left that entire part out of it cause its standard oa2 practice... the first 3 steps I don't need to check for a cookie cause the app will be sending the app specific access token along with every request... irrespective of the developer using client side or server flows for oa2! and in step 3 the js you send will be fingerprinted (in rails assets are in production by default) so you know in step 4 the origin is the js you sent. – vvohra87 Nov 22 '12 at 07:53
  • Perhaps some of this was not clear in the answer but I wanted to give an answer with an overview, explaining the entire thing with code samples is a weekend project to publish a 5 page tutorial! – vvohra87 Nov 22 '12 at 07:54
  • @Arjan Also the gem I advised has support for all this basic oa2 goodness baked right in... https://github.com/applicake/doorkeeper/wiki/Supported-Features – vvohra87 Nov 22 '12 at 07:55
  • Thank you very much for the detailed answer! As you guessed, I am using doorkeeper. One point that I could not understand from all the comments how the JS that runs in my client's domain reads the cookie that was set from my domain. Can you please clarify that? – davidrac Nov 22 '12 at 11:22
  • @davidrac take a look at this question and the answer http://stackoverflow.com/questions/402348/getting-setting-cookies-on-different-domains-with-javascript-or-other. Also, you can use a small Iframe (which is what facebook does) you can read about that here http://stackoverflow.com/questions/4701922/how-does-facebook-set-cross-domain-cookies-for-iframes-on-canvas-pages. And incase you run into issues with IE http://stackoverflow.com/questions/13473287/get-cookies-from-another-domain-in-ie – vvohra87 Nov 22 '12 at 13:02
  • Sorry, I really think to prevent using the API key on another site *and* to stop XSS, one needs to check for the domain in the JavaScript code to ensure the access token is not set on other domains, *and* that a second cookie (from your domain) needs to be set in the very same step in which that JavaScript is fetched. Also, I don't see how [setting a cookie on the site that embeds the widget](http://stackoverflow.com/questions/402348/getting-setting-cookies-on-different-domains-with-javascript-or-other) is securing anything, as that cookie will never be sent back to *your* web server? – Arjan Nov 22 '12 at 22:33
  • 1
    It seems that this is quite controversial. This is another post by @Arjan that is relevant: http://stackoverflow.com/questions/5472668/rest-authentication-and-exposing-the-api-key/13891103#13891103. Since I didn't yet implement any of the solutions, I can't yet accept any :( – davidrac Dec 15 '12 at 11:34