+3
Gemfile
+3
Gemfile
+5
Gemfile.lock
+5
Gemfile.lock
···
25
25
minitest (>= 5.1)
26
26
securerandom (>= 0.3)
27
27
tzinfo (~> 2.0, >= 2.0.5)
28
+
base58 (0.2.3)
28
29
base64 (0.3.0)
29
30
benchmark (0.4.1)
30
31
bigdecimal (3.2.2)
···
40
41
pp (>= 0.6.0)
41
42
rdoc (>= 4.0.0)
42
43
reline (>= 0.4.2)
44
+
jwt (3.1.2)
45
+
base64
43
46
logger (1.7.0)
44
47
minisky (0.5.0)
45
48
base64 (~> 0.1)
···
108
111
109
112
DEPENDENCIES
110
113
activerecord (~> 7.2)
114
+
base58
111
115
didkit (~> 0.2)!
112
116
irb
117
+
jwt
113
118
minisky (~> 0.5)
114
119
pg
115
120
puma
+68
app/server.rb
+68
app/server.rb
···
1
+
require 'base58'
2
+
require 'base64'
3
+
require 'didkit'
1
4
require 'json'
5
+
require 'jwt'
6
+
require 'openssl'
2
7
require 'sinatra/base'
3
8
4
9
require_relative 'init'
···
6
11
require_relative 'models/user'
7
12
8
13
class Server < Sinatra::Application
14
+
class PKeyCache
15
+
def self.get(did)
16
+
@cache ||= {}
17
+
@cache[did]
18
+
end
19
+
20
+
def self.set(did, pkey)
21
+
@cache ||= {}
22
+
@cache[did] = pkey
23
+
end
24
+
end
25
+
9
26
register Sinatra::ActiveRecordExtension
10
27
set :port, 3000
11
28
···
21
38
content_type :json
22
39
[status, JSON.generate({ error: name, message: message })]
23
40
end
41
+
42
+
def decode_user_from_jwt(auth_header, endpoint)
43
+
return nil unless auth_header.start_with?('Bearer ')
44
+
45
+
token = auth_header.gsub(/\ABearer /, '')
46
+
data = JSON.parse(Base64.decode64(token.split('.')[1]))
47
+
did = data['iss']
48
+
return nil if data['aud'] != 'did:web:lycan.feeds.blue' || data['lxm'] != endpoint
49
+
50
+
pkey = pkey_for_user(did)
51
+
52
+
decoded = JWT.decode(token, pkey, true, { algorithm: 'ES256K' })
53
+
decoded[0] && decoded[0]['iss']
54
+
end
55
+
56
+
def pkey_for_user(did)
57
+
# I have no idea what this does, but it seems to be working ¯\_(ツ)_/¯
58
+
59
+
if pkey = PKeyCache.get(did)
60
+
return pkey
61
+
end
62
+
63
+
doc = DID.new(did).document.json
64
+
key_obj = (doc['verificationMethod'] || []).detect { |x| x['type'] == 'Multikey' }
65
+
key_multi = key_obj&.dig('publicKeyMultibase')
66
+
return nil unless key_multi
67
+
68
+
key_decoded = Base58.base58_to_binary(key_multi[1..], :bitcoin)
69
+
comp_key = key_decoded[2..-1]
70
+
71
+
alg_id = OpenSSL::ASN1::Sequence([
72
+
OpenSSL::ASN1::ObjectId('id-ecPublicKey'),
73
+
OpenSSL::ASN1::ObjectId('secp256k1')
74
+
])
75
+
76
+
der = OpenSSL::ASN1::Sequence([alg_id, OpenSSL::ASN1::BitString(comp_key)]).to_der
77
+
pkey = OpenSSL::PKey.read(der)
78
+
79
+
PKeyCache.set(did, pkey)
80
+
81
+
pkey
82
+
end
24
83
end
25
84
26
85
get '/xrpc/blue.feeds.lycan.searchPosts' do
···
29
88
if settings.development?
30
89
user = User.find_by(did: params[:user])
31
90
return json_error('UserNotFound', 'Missing "user" parameter') if user.nil?
91
+
else
92
+
begin
93
+
did = decode_user_from_jwt(env['HTTP_AUTHORIZATION'], 'blue.feeds.lycan.searchPosts')
94
+
rescue StandardError => e
95
+
p e
96
+
end
97
+
98
+
user = did && User.find_by(did: did)
99
+
return json_error('UserNotFound', 'Missing authentication header') if user.nil?
32
100
end
33
101
34
102
if params[:query]