diff --git a/README.md b/README.md index d4b1f24..02ea2cc 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This is a real bummer. So we built Gatekeeper, which is the missing piece you ne Gatekeeper works well with [Github.js](http://github.com/michael/github), which helps you accessing the [Github API](http://developer.github.com/v3/) from the browser. ## API - + ``` GET http://localhost:9999/authenticate/TEMPORARY_CODE ``` @@ -18,7 +18,7 @@ GET http://localhost:9999/authenticate/TEMPORARY_CODE Also see the [documentation on Github](http://developer.github.com/v3/oauth/). 1. Redirect users to request GitHub access. - + ``` GET https://github.com/login/oauth/authorize ``` @@ -26,13 +26,13 @@ Also see the [documentation on Github](http://developer.github.com/v3/oauth/). 2. GitHub redirects back to your site including a temporary code you need for the next step. You can grab it like so: - + ```js var code = window.location.href.match(/\?code=(.*)/)[1]; ``` - + 3. Request the actual token using your instance of Gatekeeper, which knows your `client_secret`. - + ```js $.getJSON('http://localhost:9999/authenticate/'+code, function(data) { console.log(data.token); @@ -41,64 +41,120 @@ Also see the [documentation on Github](http://developer.github.com/v3/oauth/). ## Setup your Gatekeeper -1. Clone it +1. Clone it - ``` - git clone git@github.com:prose/gatekeeper.git - ``` + ``` + git clone git@github.com:anvaka/gatekeeper.git + ``` -2. Install Dependencies - - ``` - cd gatekeeper && npm install - ``` +2. Install Dependencies -3. Adjust config.json + ``` + cd gatekeeper && npm install + ``` - ```js - { - "client_id": "GITHUB_APPLICATION_CLIENT_ID", - "client_secret": "GITHUB_APPLICATION_CLIENT_SECRET", - "host": "github.com", - "port": 443, - "path": "/login/oauth/access_token", - "method": "POST", - "server": { - "port": 9999 - } - } - ``` +3. Adjust config.json - You can also set environment variables to override the settings if you don't want Git to track your adjusted config.json file. Just use UPPER_CASE keys. + ```js + { + "default" : { + "client_id": "GITHUB_APPLICATION_CLIENT_ID", + "client_secret": "GITHUB_APPLICATION_CLIENT_SECRET" + }, + "oauth_host": "github.com", + "oauth_port": 443, + "oauth_path": "/login/oauth/access_token", + "oauth_method": "POST" + } + ``` + + If you want to support multiple apps (e.g. one for localhost development, one + for beta and one for production), you can adjust your config with use case name: + + ```js + { + "local": { + "client_id": "GITHUB_APPLICATION_LOCAL_CLIENT_ID", + "client_secret": "GITHUB_APPLICATION_LOCAL_CLIENT_SECRET" + }, + "beta": { + "client_id": "GITHUB_APPLICATION_BETA_CLIENT_ID", + "client_secret": "GITHUB_APPLICATION_BETA_CLIENT_SECRET" + }, + "default" : { + "client_id": "GITHUB_APPLICATION_CLIENT_ID", + "client_secret": "GITHUB_APPLICATION_CLIENT_SECRET" + }, + "oauth_host": "github.com", + "oauth_port": 443, + "oauth_path": "/login/oauth/access_token", + "oauth_method": "POST" + } + ``` + + You can also set environment variables to override the settings if you don't + want Git to track your adjusted config.json file: + + ``` sh + export BETA='{"client_id": "CLIENT_ID", "client_secret": "CLIENT_SECRET"}' + ``` 4. Serve it - ``` - $ node server.js - ``` + ``` + $ node server.js + ``` ## Deploy on Heroku +0. Install [heroku CLI](https://devcenter.heroku.com/articles/heroku-command). + Login to heroku: + + ``` + heroku login + ``` + 1. Create a new Heroku app - - ``` - cake heroku:create - ``` + + ``` + heroku apps:create + ``` 2. Rename it (optional) - - ``` - heroku apps:rename NEW_NAME - ``` + + ``` + heroku apps:rename NEW_NAME + ``` 3. Provide OAUTH_CLIENT_ID and OAUTH_CLIENT_SECRET: - ``` - cake -c OAUTH_CLIENT_ID -s OAUTH_CLIENT_SECRET heroku:config - ``` + ``` + heroku config:set DEFAULT='{"client_id": "CLIENT_ID", "client_secret": "CLIENT_SECRET"}' + ``` 4. Push changes to heroku - ``` - cake heroku:push - ``` + ``` + git push heroku master + ``` + +5. Verify it is working: + + ``` + curl your_app_name.herokuapp.com/ + ``` + + Should return something like `Cannot GET /` + + To actually trade github code for an access token call: + + ``` + curl your_app_name.herokuapp.com/authenticate/code + ``` + + If you want to use a particular use case (e.g. beta), declared in your config: + + ``` + curl your_app_name.herokuapp.com/authenticate/code?case=beta + ``` + diff --git a/config.json b/config.json index 2c39adb..731c907 100644 --- a/config.json +++ b/config.json @@ -1,8 +1,14 @@ { - "oauth_client_id": "GITHUB_APPLICATION_CLIENT_ID", - "oauth_client_secret": "GITHUB_APPLICATION_CLIENT_SECRET", + "localhost" : { + "client_id": "GITHUB_APPLICATION_CLIENT_ID", + "client_secret": "GITHUB_APPLICATION_CLIENT_SECRET" + }, + "default" : { + "client_id": "GITHUB_APPLICATION_CLIENT_ID", + "client_secret": "GITHUB_APPLICATION_CLIENT_SECRET" + }, "oauth_host": "github.com", "oauth_port": 443, "oauth_path": "/login/oauth/access_token", "oauth_method": "POST" -} \ No newline at end of file +} diff --git a/server.js b/server.js index a340294..e09f771 100644 --- a/server.js +++ b/server.js @@ -1,18 +1,22 @@ -var url = require('url'), - http = require('http'), - https = require('https'), - fs = require('fs'), - qs = require('querystring'), - express = require('express'), - app = express(); +var url = require('url'), + http = require('http'), + https = require('https'), + fs = require('fs'), + qs = require('querystring'), + express = require('express'), + app = express(); // Load config defaults from JSON file. // Environment variables override defaults. function loadConfig() { - var config = JSON.parse(fs.readFileSync(__dirname+ '/config.json', 'utf-8')); - for (var i in config) { - config[i] = process.env[i.toUpperCase()] || config[i]; - } + var config = require('./config.json'); + Object.keys(config).forEach(function(key) { + var envValue = process.env[key.toUpperCase()]; + if (!envValue) return; + + config[key] = (typeof config[key] === 'object') ? JSON.parse(envValue) : envValue; + }); + console.log('Configuration'); console.log(config); return config; @@ -20,10 +24,17 @@ function loadConfig() { var config = loadConfig(); -function authenticate(code, cb) { +function authenticate(code, useCase, cb) { + var oauth = config[useCase] || config.default; + + if (!oauth) { + cb(new Error('Could not find oauth settings'), null); + return; + } + var data = qs.stringify({ - client_id: config.oauth_client_id, - client_secret: config.oauth_client_secret, + client_id: oauth.client_id, + client_secret: oauth.client_secret, code: code }); @@ -32,13 +43,17 @@ function authenticate(code, cb) { port: config.oauth_port, path: config.oauth_path, method: config.oauth_method, - headers: { 'content-length': data.length } + headers: { + 'content-length': data.length + } }; var body = ""; var req = https.request(reqOptions, function(res) { res.setEncoding('utf8'); - res.on('data', function (chunk) { body += chunk; }); + res.on('data', function(chunk) { + body += chunk; + }); res.on('end', function() { cb(null, qs.parse(body).access_token); }); @@ -46,30 +61,37 @@ function authenticate(code, cb) { req.write(data); req.end(); - req.on('error', function(e) { cb(e.message); }); + req.on('error', function(e) { + cb(e.message); + }); } // Convenience for allowing CORS on routes - GET only -app.all('*', function (req, res, next) { - res.header('Access-Control-Allow-Origin', '*'); - res.header('Access-Control-Allow-Methods', 'GET, OPTIONS'); +app.all('*', function(req, res, next) { + res.header('Access-Control-Allow-Origin', '*'); + res.header('Access-Control-Allow-Methods', 'GET, OPTIONS'); res.header('Access-Control-Allow-Headers', 'Content-Type'); next(); }); app.get('/authenticate/:code', function(req, res) { - console.log('authenticating code:' + req.params.code); - authenticate(req.params.code, function(err, token) { - var result = err || !token ? {"error": "bad_code"} : { "token": token }; - console.log(result); - res.json(result); - }); + console.log('authenticating code:', req.params.code); + console.log('use case: ', req.query.case); + authenticate(req.params.code, req.query.case, function(err, token) { + var result = err || !token ? { + "error": "bad_code" + } : { + "token": token + }; + console.log(result); + res.json(result); + }); }); var port = process.env.PORT || config.port || 9999; -app.listen(port, null, function (err) { +app.listen(port, null, function(err) { console.log('Gatekeeper, at your service: http://localhost:' + port); });