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