Skip to content

Instantly share code, notes, and snippets.

@maxwell
Created March 11, 2012 09:15
Show Gist options
  • Save maxwell/2015703 to your computer and use it in GitHub Desktop.
Save maxwell/2015703 to your computer and use it in GitHub Desktop.
WhoGrantSig...a really simple authenticated request system using public + private keys and webfinger for discovery
require 'rubygems'
require 'openssl'
require 'faraday'
#a fake user has a keypair and a email like idenitifier which responds
# to webfinger for discovery
class FakeUser
attr_accessor :key, :diaspora_handle
def initialize
self.key = new_key
self.diaspora_handle = "[email protected]"
end
def new_key
@key ||= OpenSSL::PKey::RSA::generate(512)
end
def public_key
key.public_key
end
def encryption_key
key
end
alias :private_key :encryption_key
end
## who grant sig
## take the account, who has a keypair discovered by webfinger,
## and create a 'grant' for them
# this is just a simple signable string, which includes the name itself,
# and a timeout to prevent the grant from being used too far in the future
# with these 3 pieces of information, any server can verify who exactly is making
# a request.
#in many ways, this is just an over simplified SALMON protocol, but with the idea
#that you could use a simmilar mechinism for GETs
#this could be useful in server to server requests, or even requests
#sourced from the client, possibly CORS, or in OEMBED
#we may be able to use this for authenticated CORS POST requests???(must include
#post body)
class WhoGrantSig
attr_accessor :user, :diaspora_id
def initialize(user)
self.user = user
self.diaspora_id = self.user.diaspora_handle
end
def header_hash
{
'from:' => diaspora_id,
'X-WhoGrantSig-signature:' => id_signature,
'X-WhoGrantSig-valid-until' => time
}
end
def from_header
"from: #{diaspora_id}"
end
def signature_header
"X-WhoGrantSig-signature: #{id_signature}"
end
def time_header
"X-WhoGrantSig-valid-until: #{time}"
end
def time
@time ||= (Time.now + 5.minutes).to_s
end
def id_string
[diaspora_id, time].join(',')
end
def id_signature
user.encryption_key.sign(OpenSSL::Digest::SHA256.new, id_string)
end
end
## validates who sent the request. verify who is sending you this request,
## and give them access to what you want to
class WhoGrantSigValidator
attr_accessor :from_header, :signature_header, :time_header
def initialize(from_header, signature_header, time_header)
self.from_header = from_header
self.signature_header = signature_header
self.time_header = time_header
@from = Webfinger.new(from).fetch
end
def valid?
signature_is_valid? && grant_is_still_valid?
end
private
def signature
signature_header.strip!
end
def from
from_header.strip!
end
def public_key
@from.public_key
end
def time
Time.parse(time_header)
end
def grant_string
[from_header, time.to_s].join(',')
end
def signature_is_valid?
public_key.verify(OpenSSL::Digest::SHA256.new, signature, grant_string)
end
def grant_is_still_valid?
time < Time.now + 5.mintues
end
end
# a simple http client for testing
class AuthenticatedRequest
def intialize(who_grant_sig)
@sig = who_grant_sig
end
def get(url)
connection.get(url) do |request|
@sig.headers.each do |key, value|
request.headers[key] = value
end
end
end
private
def connection
conn = Faraday.new do |builder|
builder.request :url_encoded
builder.response :logger
builder.adapter :net_http
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment