Skip to content

Instantly share code, notes, and snippets.

@max-mapper
Created June 3, 2012 02:23
Show Gist options
  • Save max-mapper/2861012 to your computer and use it in GitHub Desktop.
Save max-mapper/2861012 to your computer and use it in GitHub Desktop.
github oauth with tako and leveldb
var path = require('path')
var tako = require('tako')
var gist = require('gist')
var request = require('request')
var qs = require('querystring')
var leveldb = require('leveldb')
var https = require('https')
var htmldir = path.resolve(__dirname, 'attachments')
var t = tako()
t.templates.directory(path.resolve(__dirname, 'templates'))
var options = {
clientID: process.env['GITHUB_KEY'],
clientSecret: process.env['GITHUB_SECRET'],
callbackURL: process.env['VHOST'] + "/githuboauthcallback"
}
t.route('/', function (req, resp) {
var page = t.page()
page.template('index')
page.on('finish', function(results) {
results.user = req.user
})
req.pipe(page).pipe(resp)
})
t.route('/me', function (req, resp) {
getProfile(req.user.token, function(err, data) {
resp.end(data)
})
}).must('auth')
t.route('/logout', function (req, resp) {
setCookie('', resp)
resp.statusCode = 302
resp.setHeader('location', process.env['VHOST'])
resp.end()
})
t.route('/githuboauth', function (req, resp) {
var u = 'https://github.com/login/oauth/authorize'
+ '?client_id=' + options.clientID
+ '&redirect_uri=' + options.callbackURL
+ '&scope=user,public_repo,repo,gist'
resp.statusCode = 302
resp.setHeader('location', u)
resp.end()
})
t.route('/githuboauthcallback', function (req, resp) {
var reqBody = {
client_id: options.clientID,
client_secret: options.clientSecret,
redirect_uri: options.callbackURL,
code: req.qs.code
}
request.post({
uri: 'https://github.com/login/oauth/access_token',
headers: {
"content-type": "application/x-www-form-urlencoded",
"accept": "application/json"
},
body: qs.encode(reqBody)
},
function (e, r, body) {
resp.statusCode = 200
var data = JSON.parse(body)
var token = data.access_token
setCookie(token, resp)
saveUser(token, resp)
}
)
})
t.route('/*').files(htmldir)
t.auth(function(req, resp, cb) {
var token = extractToken(req)
if (!token) return cb(null)
getDB('users', function(err, db) {
db.get(token, function(err, data) {
if (err) return cb(null)
if (!data) return cb(null)
cb(JSON.parse(data))
})
})
})
function getProfile(token, cb) {
var options = {
host: 'api.github.com',
port: 443,
path: '/user?' + qs.stringify({access_token: token}),
method: 'GET'
}
var req = https.request(options, function(res) {
var resp = ""
res.on('data', function(d) {
resp += d.toString()
})
res.on('end', function() { cb(false, JSON.parse(resp)) })
res.on('error', function(err) { cb(err) })
})
req.end()
}
function saveUser(token, resp) {
getDB('users', function(err, db) {
db.put(token, JSON.stringify({token: token}), function(err) {
if (err) {
resp.statusCode = 500
return resp.end('error saving user')
}
resp.statusCode = 302
resp.setHeader('location', process.env['VHOST'])
resp.end()
})
})
}
function getDB(name, cb) {
if (!t.dbs) t.dbs = {}
if (t.dbs[name]) return cb(null, t.dbs[name])
leveldb.open(name + ".leveldb", { create_if_missing: true }, function(err, db) {
if (err) return cb(err)
t.dbs[name] = db
cb(null, db)
})
}
function setCookie(id, resp) {
var twoWeeks = new Date(new Date().getTime()+1209726000).toUTCString()
resp.setHeader('set-cookie', ['Token='+id + '; Version=1; Path=/; HttpOnly; Expires=' + twoWeeks])
resp.setHeader('x-token', id)
}
function extractToken(req) {
if (req.headers.cookie) {
var cookies = parseCookies(req.headers.cookie)
if (cookies['Token']) return cookies['Token']
}
if (req.headers['x-token']) return req.headers['x-token']
if (req.qs && req.qs.token) return req.qs.token
return false
}
function parseCookies(cookie) {
var cookies = {}
cookie.split(';').forEach(function( cookie ) {
var parts = cookie.split('=')
cookies[ parts[ 0 ].trim() ] = ( parts[ 1 ] || '' ).trim()
})
return cookies
}
t.httpServer.listen(8000, function () {
console.log('dun runnin')
})
@mikeal
Copy link

mikeal commented Jun 5, 2012

what leveldb bindings are you using? there are like 3 that are all incompatible forks, some of which aren't in npm.

@max-mapper
Copy link
Author

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