Created
March 11, 2012 09:15
-
-
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
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 '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