Don't forget to lycansubscribe
1require 'base58' 2require 'base64' 3require 'didkit' 4require 'json' 5require 'jwt' 6require 'openssl' 7 8class Authenticator 9 def initialize(hostname:) 10 @@pkey_cache ||= {} 11 @hostname = hostname 12 end 13 14 def decode_user_from_jwt(auth_header, endpoint) 15 return nil unless auth_header.start_with?('Bearer ') 16 17 token = auth_header.gsub(/\ABearer /, '') 18 data = JSON.parse(Base64.decode64(token.split('.')[1])) 19 did = data['iss'] 20 return nil if data['aud'] != "did:web:#{@hostname}" || data['lxm'] != endpoint 21 22 pkey = pkey_for_user(did) 23 24 decoded = JWT.decode(token, pkey, true, { algorithm: 'ES256K' }) 25 decoded[0] && decoded[0]['iss'] 26 end 27 28 def pkey_for_user(did) 29 # I have no idea what this does, but it seems to be working ¯\_(ツ)_/¯ 30 31 if pkey = @@pkey_cache[did] 32 return pkey 33 end 34 35 doc = DID.new(did).document.json 36 key_obj = (doc['verificationMethod'] || []).detect { |x| x['type'] == 'Multikey' } 37 key_multi = key_obj&.dig('publicKeyMultibase') 38 return nil unless key_multi 39 40 key_decoded = Base58.base58_to_binary(key_multi[1..], :bitcoin) 41 comp_key = key_decoded[2..-1] 42 43 alg_id = OpenSSL::ASN1::Sequence([ 44 OpenSSL::ASN1::ObjectId('id-ecPublicKey'), 45 OpenSSL::ASN1::ObjectId('secp256k1') 46 ]) 47 48 der = OpenSSL::ASN1::Sequence([alg_id, OpenSSL::ASN1::BitString(comp_key)]).to_der 49 pkey = OpenSSL::PKey.read(der) 50 51 @@pkey_cache[did] = pkey 52 53 pkey 54 end 55end