tangled
alpha
login
or
join now
mackuba.eu
/
lycan
35
fork
atom
Don't forget to lycansubscribe
35
fork
atom
overview
issues
1
pulls
pipelines
added jwt-based authentication
mackuba.eu
6 months ago
14fdb73f
76959cc6
+76
3 changed files
expand all
collapse all
unified
split
Gemfile
Gemfile.lock
app
server.rb
+3
Gemfile
···
12
12
gem 'minisky', '~> 0.5'
13
13
gem 'didkit', '~> 0.2', git: 'https://tangled.sh/@mackuba.eu/didkit'
14
14
15
15
+
gem 'base58'
16
16
+
gem 'jwt'
17
17
+
15
18
group :development do
16
19
gem 'puma'
17
20
gem 'rackup'
+5
Gemfile.lock
···
25
25
minitest (>= 5.1)
26
26
securerandom (>= 0.3)
27
27
tzinfo (~> 2.0, >= 2.0.5)
28
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
44
+
jwt (3.1.2)
45
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
114
+
base58
111
115
didkit (~> 0.2)!
112
116
irb
117
117
+
jwt
113
118
minisky (~> 0.5)
114
119
pg
115
120
puma
+68
app/server.rb
···
1
1
+
require 'base58'
2
2
+
require 'base64'
3
3
+
require 'didkit'
1
4
require 'json'
5
5
+
require 'jwt'
6
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
14
+
class PKeyCache
15
15
+
def self.get(did)
16
16
+
@cache ||= {}
17
17
+
@cache[did]
18
18
+
end
19
19
+
20
20
+
def self.set(did, pkey)
21
21
+
@cache ||= {}
22
22
+
@cache[did] = pkey
23
23
+
end
24
24
+
end
25
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
41
+
42
42
+
def decode_user_from_jwt(auth_header, endpoint)
43
43
+
return nil unless auth_header.start_with?('Bearer ')
44
44
+
45
45
+
token = auth_header.gsub(/\ABearer /, '')
46
46
+
data = JSON.parse(Base64.decode64(token.split('.')[1]))
47
47
+
did = data['iss']
48
48
+
return nil if data['aud'] != 'did:web:lycan.feeds.blue' || data['lxm'] != endpoint
49
49
+
50
50
+
pkey = pkey_for_user(did)
51
51
+
52
52
+
decoded = JWT.decode(token, pkey, true, { algorithm: 'ES256K' })
53
53
+
decoded[0] && decoded[0]['iss']
54
54
+
end
55
55
+
56
56
+
def pkey_for_user(did)
57
57
+
# I have no idea what this does, but it seems to be working ¯\_(ツ)_/¯
58
58
+
59
59
+
if pkey = PKeyCache.get(did)
60
60
+
return pkey
61
61
+
end
62
62
+
63
63
+
doc = DID.new(did).document.json
64
64
+
key_obj = (doc['verificationMethod'] || []).detect { |x| x['type'] == 'Multikey' }
65
65
+
key_multi = key_obj&.dig('publicKeyMultibase')
66
66
+
return nil unless key_multi
67
67
+
68
68
+
key_decoded = Base58.base58_to_binary(key_multi[1..], :bitcoin)
69
69
+
comp_key = key_decoded[2..-1]
70
70
+
71
71
+
alg_id = OpenSSL::ASN1::Sequence([
72
72
+
OpenSSL::ASN1::ObjectId('id-ecPublicKey'),
73
73
+
OpenSSL::ASN1::ObjectId('secp256k1')
74
74
+
])
75
75
+
76
76
+
der = OpenSSL::ASN1::Sequence([alg_id, OpenSSL::ASN1::BitString(comp_key)]).to_der
77
77
+
pkey = OpenSSL::PKey.read(der)
78
78
+
79
79
+
PKeyCache.set(did, pkey)
80
80
+
81
81
+
pkey
82
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
91
+
else
92
92
+
begin
93
93
+
did = decode_user_from_jwt(env['HTTP_AUTHORIZATION'], 'blue.feeds.lycan.searchPosts')
94
94
+
rescue StandardError => e
95
95
+
p e
96
96
+
end
97
97
+
98
98
+
user = did && User.find_by(did: did)
99
99
+
return json_error('UserNotFound', 'Missing authentication header') if user.nil?
32
100
end
33
101
34
102
if params[:query]