Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save jcouyang/9362622 to your computer and use it in GitHub Desktop.
Save jcouyang/9362622 to your computer and use it in GitHub Desktop.
pure and secure javascript oauth with yql

It would be awesome if we can use OAuth in JavaScript purely in client side. before start to do that, please let me explain “OAuth2” with this picture in feeeew word (skip to section 2 YQL if you know OAuth2):

http://hueniverse.com/wp-content/uploads/2007/12/My-Endpoints-300x267.png

OAuth 2

OAuth 2 is widely use as authorize third party application without expose user’s password, OAuth2 using 2 steps verification. Take github as example:

There are 2 role in this story: Developer Oyang and User Lulu

As Developer Oyang who write an App GIRA http://gira,oyanglul.us which can let user login using Github account.

As User, Lulu don’t want to login in your site with my Github username and password, since Lulu don’t trust Oyang but trust Github.

Solution should be: Oyang redirect Lulu to Github website so Lulu can login there, then github redirect to Oyang’s gira.com and with a signal: “hey, lulu’s password is correct, log she in dude!”.

So that is OAuth 2 actually.

Redirect users to request GitHub access

GET https://github.com/login/oauth/authorize

with parameter

  • cliend_id: id of your application
  • scope: permissions your application should have

this will redirect Lulu to github oauth page. Lulu can now login in github then check the permission Oyang’s app request, and decide to accept or not.

GitHub redirects back to your site

if Lulu say “yes”, github will redirect back to Oyang’s app with parameter of a “valided code”. which means apps’s permited and now app can get the Lulu’s access_token now.

POST https://github.com/login/oauth/access_token

with parameter

  • cliend_id: id of your application
  • client_secret: password of your application
  • code: the thing you just got from github.

access_token is just as important as User’s PASSWORD so keep that saft place and never expose to anyone.

client_secret is your app’s password so never expose to anyone either

Done

now Oyang’s App Gira just got the access_token of Lulu, so Gira can make requests to the API on a behalf of a Lulu.

Pure Client Side Implement

Now here comes the problems of Javascript OAuth 2 implementation pure client side.

  1. capture code in step 2
  2. secure client_secret when try request access_token

when github redirect back with code

the first one is easy to solve by check window.location

$(window).on('eshashchange', function() {
  if(location.hash='#authdone'){
		$.get("https://github.com/login/oauth/access_token",{
			client_id:'666dc0b3b994cc362ca2',
			client_secret:'your secret',
			code:location.search.split('=').pop();
		});
	}
});

as you can see the client_secret has been expose to anyone who check you javascript code.

This is really serious problem since evil hacker can just use your client_id and client_secret to make an fake app of gira to steal your user’s information, horrible isn’t it.

now what, anyone can easily see your javascript from browser, where to hide your secret. no option but a server.

YQL

What is YQL

YQL is Yahoo Query Language. You can simpily use SELECT * FROM web to get you data from any website. for example you can try put the follow query in YQL Console:

select * from html where urlhttp://www.weibo.com/milhouse”=

amazing isn’t it, YQL will return the whole content of the website in XML or JSON.

check the bottom of YQL Console, simpily use request to THE REST QUERY will return the same thing. You don’t have to include any other third party annoying library to get your data. This is why I choose YQL other then Parse or Firebase as server side script.

Use YQL storage to keep secret safe

YQL provide online storage y.storage which allow you to store your YQL table, javascript and enviorment there. Since every thing is on sever, nobody but you can see them now.

When open YQL editor, you may curious about the 3 Key on the right side:

https://www.evernote.com/shard/s23/sh/9428c885-f033-46c9-882d-3527ee12711f/30139b47807b08c5a6133bf3769c29d6/deep/0/YQL-Editor--asdf.png

for each table/javascript/enviorment file you’ve create, there are 3 line for you.

  1. EXECUTE: use this link when you want to execute the content.this is really important for secure your secret, I’ll explain it latter.
  2. SELECT: when you just want to get the content.
  3. UPDATE: when update the content.

for better understanding, let me continue the Github OAuth example.

Here’s the plan:

  1. put all you secret inside enviorment file.
  2. create a table, data of the table come from javascript file,
  3. when the javascript is execute, request for the access_token
  4. on the clientside, just request the YQL table for access_token. bang!

Create YQL Table

OK.lets do IT. First of all, we need create a table who can execute Javascript inside.

<?xml version="1.0" encoding="UTF-8"?>
	<table xmlns="http://query.yahooapis.com/v1/schema/table.xsd">	  
      <meta>  
        <sampleQuery>select * from {table} where code='meow';</sampleQuery>  
      </meta>  
      <bindings>  
        <select itemPath="" produces="XML">  
          <urls>  
            <url>http://oyanglul.us/gira</url>  
          </urls>  
          <inputs>  
            <key id='CODE' type='xs:string' paramType='variable' required="true" />(ref:code)  
          <key id="CID" type="xs:string" paramType="variable"  required="true" />(ref:client-id)
              <key id="CSC" type="xs:string" paramType="variable"  required="true" />(ref:client-secret)
            </inputs>
            <execute><![CDATA[
         y.include('store://KqAGbe0nt2yi3bAnQQXxOx'); (ref:js-select)
      ]]></execute>         
        </select>    
      </bindings>  
    </table>

FYI, the line (js-select) reference to the SELECT KEY of the javascript file as follow, why SELECT, you know when you use EXECUTE KEY to referent an file, Yahoo will try to run it for you, but I don’t want the result of javascript but the code itself to define my table.

line (code) define the table should receive a key named “CODE”, and line (client-id) and line (client-secret) as well.

Create Javascript file

tokenRequest = y.rest('https://github.com').path('login').path('oauth').path('access_token');(ref:y-rest)
var resp = tokenRequest.header('Accept','application/xml').query(
{
	client_id:CID,
	client_secret:CSC,
	code:CODE
}).post().response;
response.object = {resp}

I know line (y-rest) is weird if you never use YQL before, so do I. This looks so lame to append path to address rather then jQuery way just $.get("https://github.com/login/oauth/access_token").

ok the CID is parameter from YQL Table defined here, so does CSC and CODE.

finally, the Table and Javascript is done, how to use them, and where the hell should I put my secret to.

Create Enviroment File

Here comes the mighty enviorment file:

USE "store://jqozna9Rv9K0gS77jz8RI1" AS github;(ref:github-table)
SET CID="666dc0b3b994cc362ca2" ON github; (ref:set-cid)
SET CSC="your client secret goes here" ON github;(ref:set-csc)

the store://jqozna9Rv9K0gS77jz8RI1 is the SELECT KEY of your table just created. line (set-cid) and line set-csc pass the client_id and client_secret to github table where the javascript can actually use.

Why My Secret is secure

if you use the SELECT KEY of the enviorment file like

select * from yql.storage where name="your enviorment file SELECT KEY"

the secret defined in your enviorment file will still expose.

but not one know your SELECT KEY except yourself. so you never use the SELECT KEY everything will be safe.

Thus, use the EXECUTE KEY!!!! no one can know what happen inside your enviorment file.

env "store://0zaLUaPXLo4GWBb1koVqO6";
select * from github where CODE="code from oauth first step"

Fin

copy the https://www.evernote.com/shard/s23/sh/4a383e94-4288-4ad1-a686-f8d63b5fa4cc/20d4672a6cc52e0c1e99c6250ea583dd/deep/0/YQL-Console--env- at the bottom, request this url from you client side javascript code. That’s is, without expose client_secret safely get access_token from pure client side javascript.

@jcouyang
Copy link
Author

@formula1
Copy link

The above tutorial was freaking awesome!!!!!!! :DDDD

I just followed through with this as its probably the most secure method I've found. I'm going to basically repeat everything you wrote. Thank you so much for creating this gist. It was spectacular.

Create your Application

1) Go here -> https://github.com/settings/applications
2) Register new application
3) fill out the form
4) Submit

For more help see https://developer.github.com/guides/getting-started/

Create your pages scripts

For My purposes, I created a github page

1) Provide a way for the user to login

You'll be sending the user to https://github.com/login/oauth/authorize?client_id=YOUR_CLIENT_ID

You can do this via

  • window.open(url) - API, StackOverflow - What is useful about this is that its a popup
  • window.location.href = url - API StackOverflow - This is just a redirect. Essentially the same as creating an anchor tag
  • <a href="url" >Click Here</a> - Just trying to point out how it doesn't matter how it gets done, just that it gets done.

I would also suggest you provide a state parameter and storing that locally until the user comes back to maximize as much security as possible

2) Check if we already have a user

We stored something on the persons computer, now lets verify

  • document.cookie - API StackOverflow - cookies are an option however that has problems in the EU. In addition its important to parse the cookie
  • localStorage.getItem("access_token") - API - This is simple however not always available

If we do have a user, we want to make sure we add "access_token="+store.get("access_token") to every api request we make to github.

3) Check to see if github is allowing us to login.

Here we need to get the code out of the url http://YOUR_URL.COM?code=the_code

This can be done via

If we do, we're going to be making a call to YQL. When making a call to YQL, this is what I liked

var query = "env \"store://YOUR_ENVIRONMENT_EXECUTE\"; SELECT * FROM github WHERE CODE=\""+window_url.query.code+"\""

var url = "https://query.yahooapis.com/v1/public/yql?q="+encodeURIComponent(query)+"&format=json

Make a get request. If there's an error, lets handle it. Otherwise, we store the access token and do our "on login" logic.

get(url).then(function(jsonObject){
  if(jsonObject.query.results.OAuth.error){
    return handleError(jsonObject.query.results.OAuth);
  }
  store.set("access_token", jsonObject.query.results.OAuth.access_token);
  //on login logic
}).catch(handleError);

Create your YQL Application

1) Create your table

Must be created at https://developer.yahoo.com/yql/editor

<?xml version="1.0" encoding="UTF-8"?>
<table xmlns="http://query.yahooapis.com/v1/schema/table.xsd">
    <meta>
        <sampleQuery>select * from {table} where code='meow';</sampleQuery>
    </meta>  
    <bindings>
        <select itemPath="" produces="XML">
            <urls>
                <url>http://oyanglul.us/gira</url>
            </urls>
            <inputs>
                <key id='CODE' type='xs:string' paramType='variable' required="true" />
                <key id="CID" type="xs:string" paramType="variable"  required="true" />
                <key id="CSC" type="xs:string" paramType="variable"  required="true" />
            </inputs>
            <execute><![CDATA[
                y.include('store://YOUR_JAVASCRIPT_SELECT_KEY');
            ]]></execute>         
        </select>
    </bindings>  
</table>

y.include() - API is pretty sweet

2) Create your javascript

Must be created at https://developer.yahoo.com/yql/editor


tokenRequest = y.rest('https://github.com')
    .path('login')
    .path('oauth')
    .path('access_token');
response.object = tokenRequest.header('Accept','application/xml').query({
    client_id:CID,
    client_secret:CSC,
    code:CODE
}).post().response;

response.object is what YQL will be sent back to you as "results" - API

Technically you can actually make it a one liner...

response.object = y.rest('https://github.com')
.path('login').path('oauth').path('access_token')
.header('Accept','application/xml').query({
  client_id:CID,
  client_secret:CSC,
  code:CODE
}).post().response;

Apparently (And I should be corrected if I'm wrong) but All of the <inputs> get created as global variables for any executed javascript within the same <select>. Interesting stuff

3) Create your Environment

Must be created at https://developer.yahoo.com/yql/editor

USE "store://YOUR_TABLE_EXECUTE_KEY" AS github;
SET CID="YOUR_GITHUB_CLIENT_ID" ON github;
SET CSC="YOUR_GITHUB_SECRET" ON github;
4) Test with a Query

Must be tested at https://developer.yahoo.com/yql/console

env "store://YOUR_ENVIRONMENT_EXECUTE"; SELECT * FROM github WHERE CODE="a_code"

You can use the url that they give you at the bottom, however theres only a few parts I chose to care about from the url

  • https://query.yahooapis.com/v1/public/yql
  • q=a_uri_encoded_string
  • format=json

in addition, its important to test this so you know how to create your error handling scripts when github says no.

Why choose this over hello.js or oAuth.io?

The reasons I chose it was.

  • Treasure Chest of Secrets vs Dumpster full of tools and maybe a secret or two - I'd rather have my secrets get lost in a sea of scrapper apps, financial apps and other generic applications that may have nothing to do with what I'm doing. If I store my secret on something that is specifically dedicated to it, well, a hacker knows exactly what it will find if they are successful.
  • Yahoo isn't going out of business any time soon (I hope) - hello.js is on heroku -> will cost the hoster money if it becomes popular -> will force them to charge or take it down. oAuth.io has a pricing plan thats pretty reasonable but it's a business and they may take down the server when they choose. Yahoo is free, a monolith and many many people will complain if something goes bad.
  • I'm in control - This is both a good and bad thing. However, it gives me the opportunity to rebuild the wheel
  • YQL can be used for many things - YQL is actually quite interesting and I may use it for future projects now that I know it exists.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment