Created
July 11, 2012 18:46
-
-
Save coffeeaddict/3092309 to your computer and use it in GitHub Desktop.
Add a doorkeeper session token to the current user
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ApplicationControler < AC::Base | |
include FayeHelper | |
# ... existing code | |
before_filter { | |
return if current_user.nil? | |
if current_user.refresh_session_token | |
# make a new client with the new token | |
client = setup_faye_client | |
client.publish("/users/#{current_user.id}", { update_token: true }); | |
end | |
} | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var faye_client = new Faye.Client('http://<%= faye_host %>:9292/faye'); | |
<% if current_user %> | |
var clientAuth = { | |
authToken: '<%= current_user.session_token.token %>', | |
authUser: <%= current_user.id %>, | |
outgoing: function(message, callback) { | |
// all meta messages except subscribe are allowed w/o auth | |
if ( message.channel.match(/^\/meta/) != null && message.channel != '/meta/subscribe' ) { | |
return callback(message); | |
} | |
if ( typeof(message.ext) != 'object' ) message.ext = {}; | |
message.ext.authToken = clientAuth.authToken; | |
message.ext.authUser = clientAuth.authUser; | |
// console.log(message); | |
return callback(message); | |
} | |
}; | |
faye_client.addExtension(clientAuth); | |
var sub = faye_client.subscribe("/users/<%= current_user.id %>", function ( | |
if ( message.update_token ) { | |
// fetch a resource which will update clientAuth.updateToken | |
$.get('<%= user_session_token_path %>'); | |
} | |
}); | |
<% end %> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ClientAuth | |
MASTER_TOKEN = 'something secret for ultimatly trusted clients'; | |
def initialize(user) | |
@user = user | |
end | |
def outgoing(message, callback) | |
@client.debug 'client-auth: outgoing' | |
# Again, leave non-subscribe messages alone | |
if message['channel'] =~ /^\/meta\// && message['channel'] != '/meta/subscribe' | |
@client.debug 'client-auth is skipped for ?', message['channel'] | |
return callback.call(message) | |
end | |
# Add ext field if it's not present | |
message['ext'] ||= {} | |
# Set the auth token | |
if @user | |
message['ext']['authUser'] = @user.id | |
message['ext']['authToken'] = @user.session_token.token | |
else | |
# just use the master token when no user is given | |
# | |
# add some security through obscurity to prevent the worst script kiddies | |
# | |
time = Time.now.to_i | |
message['ext']['authUser'] = time | |
message['ext']['authToken'] = MASTER_TOKEN + time.to_s(16) | |
end | |
# Carry on and send the message to the server | |
return callback.call(message) | |
end | |
def added(client) | |
client.debug 'client-auth was added' | |
@client = client | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require './lib/faye_client_auth' | |
module FayeHelper | |
def faye_client | |
return setup_faye_client if @faye_client.nil? | |
@faye_client | |
end | |
def setup_faye_client | |
if !EM.reactor_running? | |
Thread.new { EM.run {} } | |
while !EM.reactor_running? | |
Rails.logger.debug "Waiting for a reactor" | |
sleep 1 | |
end | |
end | |
# this is a great help when debugging, but it will clutter your logs. | |
#if !Rails.env.production? | |
# Faye::Logging.log_level = :debug | |
# Faye.logger = lambda { |m| Rails.logger.debug m } | |
#end | |
client = Faye::Client.new("http://0.0.0.0:9292/faye") | |
user = if defined? current_user | |
current_user | |
else | |
nil | |
end | |
client.add_extension(ClientAuth.new(user)) | |
return ( @faye_client = client ) | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var faye = require('faye'), | |
redis = require('faye-redis'), | |
http = require('http'), | |
mysql = require('./mysql_connection.js'); | |
var MASTER_TOKEN = 'something secret ...'; | |
var server = http.createServer(); | |
var bayeux = new faye.NodeAdapter({ | |
mount: '/faye', | |
timeout: 25, | |
engine: { | |
type: redis, | |
host: 'localhost', | |
} | |
}); | |
var serverAuth = { | |
cache: {}, | |
incoming: function(message, callback) { | |
// Let all meta messages through | |
if (message.channel.match(/^\/meta/) != null && message.channel != "/meta/subscribe" ) { | |
return callback(message); | |
} | |
if (typeof(message.ext) != 'object') { | |
message.error = "invalid message"; | |
return callback(message); | |
} | |
var authToken = message.ext.authToken, | |
authUser = message.ext.authUser, | |
logToken = message.ext.authToken.slice(0,9) + "..."; | |
invalidate = function(msg) { | |
console.log(msg); | |
message.error = "Invalid token: " + authToken; | |
}; | |
validate = function(expires_at) { | |
console.log(logToken + " is valid until " + new Date(expires_at)); | |
serverAuth.cache[authToken] = expires_at; | |
}; | |
if ( serverAuth.cache[authToken] !== undefined ) { | |
// check if not expired and let through | |
var now = new Date(); | |
if (now.getTime() > serverAuth.cache[authToken]) { | |
invalidate(logToken + " has expired " + (now.getTime() - serverAuth.cache[authToken]) + " seconds ago"); | |
} | |
return callback(message); | |
} | |
if ( authToken == (MASTER_TOKEN + authUser.toString(16)) ) { | |
console.log("master token received"); | |
// someone handed a master token: must be a trusted client | |
return callback(message); | |
} | |
var sql = 'SELECT `oauth_access_tokens`.* FROM `oauth_access_tokens` ' + | |
' WHERE `oauth_access_tokens`.`resource_owner_id` = ?' + | |
' AND `oauth_access_tokens`.`token` = ?' + | |
' AND `oauth_access_tokens`.`application_id` = 0 LIMIT 1'; | |
var database = mysql.connect(); | |
database.query(sql, [authUser, authToken], function(err, rows) { | |
// there are no errors and at least 1 row | |
if ( err !== null ) return invalidate("errors occured in db.query: " + err); | |
if ( rows.length == 0 ) return invalidate("no results for " + authToken + "//" + authUser); | |
// the token has not been revoked | |
var access_token = rows[0]; | |
if ( access_token.revoked_at !== null ) return invalidate(logToken + " was revoked"); | |
// the token has not expired | |
var now = new Date(); | |
var expires_at = ( access_token.created_at.getTime() - (now.getTimezoneOffset() * 60 * 1000) + (access_token.expires_in * 1000) ), | |
now_time = now.getTime(); | |
if ( now_time > expires_at ) return invalidate(logToken + " has expired " + ((now_time - expires_at) / 1000) + " seconds ago"); | |
validate(expires_at); | |
}); | |
database.end(); | |
return callback(message);destroy | |
}, | |
}; | |
bayeux.addExtension(serverAuth); | |
console.log("bayeux setup complete"); | |
bayeux.listen(9292); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// npm install [email protected] | |
var mysql = require('mysql'); | |
exports.connect = function() { | |
var database; | |
if ( process.env.RAILS_ENV == 'production' ) { | |
database = mysql.createConnection({ | |
host : 'localhost', | |
user : 'production', | |
password : 'production', | |
database : 'production', | |
socketPath : '/var/run/mysqld/mysqld.sock', | |
}); | |
} else if ( process.env.RAILS_ENV == 'test' ) { | |
database = mysql.createConnection({ | |
host : 'localhost', | |
user : 'test', | |
password : 'test', | |
database : 'test', | |
socketPath : '/var/run/mysqld/mysqld.sock', | |
}); | |
} else { | |
database = mysql.createConnection({ | |
host : 'localhost', | |
user : 'root', | |
database : 'development', | |
socketPath : '/var/run/mysqld/mysqld.sock', | |
}); | |
} | |
database.connect(); | |
return database; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class User < AR::Base | |
# ... existing code | |
has_one :session_token, | |
:class_name => "Doorkeeper::AccessToken", | |
:foreign_key => :resource_owner_id, | |
:conditions => { application_id: 0 } | |
# @return [TrueClass,FalseClass] true if refreshed | |
def refresh_session_token | |
return false if !session_token.nil? && !session_token.expired? | |
# destroy the old token | |
session_token.destroy unless session_token.nil? | |
# create a new short-lived token | |
create_session_token( expires_in: 900 ) | |
return true | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment