require 'base58' require 'base64' require 'didkit' require 'json' require 'jwt' require 'openssl' class Authenticator def initialize(hostname:) @@pkey_cache ||= {} @hostname = hostname end def decode_user_from_jwt(auth_header, endpoint) return nil unless auth_header.start_with?('Bearer ') token = auth_header.gsub(/\ABearer /, '') data = JSON.parse(Base64.decode64(token.split('.')[1])) did = data['iss'] return nil if data['aud'] != "did:web:#{@hostname}" || data['lxm'] != endpoint pkey = pkey_for_user(did) decoded = JWT.decode(token, pkey, true, { algorithm: 'ES256K' }) decoded[0] && decoded[0]['iss'] end def pkey_for_user(did) # I have no idea what this does, but it seems to be working ¯\_(ツ)_/¯ if pkey = @@pkey_cache[did] return pkey end doc = DID.new(did).document.json key_obj = (doc['verificationMethod'] || []).detect { |x| x['type'] == 'Multikey' } key_multi = key_obj&.dig('publicKeyMultibase') return nil unless key_multi key_decoded = Base58.base58_to_binary(key_multi[1..], :bitcoin) comp_key = key_decoded[2..-1] alg_id = OpenSSL::ASN1::Sequence([ OpenSSL::ASN1::ObjectId('id-ecPublicKey'), OpenSSL::ASN1::ObjectId('secp256k1') ]) der = OpenSSL::ASN1::Sequence([alg_id, OpenSSL::ASN1::BitString(comp_key)]).to_der pkey = OpenSSL::PKey.read(der) @@pkey_cache[did] = pkey pkey end end