Don't forget to lycansubscribe

refactored/cleaned up authentication

Changed files
+57 -21
app
+14 -2
app/authenticator.rb
··· 6 6 require 'openssl' 7 7 8 8 class Authenticator 9 + class InvalidTokenError < StandardError 10 + end 11 + 9 12 def initialize(hostname:) 10 13 @@pkey_cache ||= {} 11 14 @hostname = hostname ··· 15 18 return nil unless auth_header.start_with?('Bearer ') 16 19 17 20 token = auth_header.gsub(/\ABearer /, '') 18 - data = JSON.parse(Base64.decode64(token.split('.')[1])) 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 + 19 31 did = data['iss'] 20 - return nil if data['aud'] != "did:web:#{@hostname}" || data['lxm'] != endpoint 32 + return nil if did.nil? || data['aud'] != "did:web:#{@hostname}" || data['lxm'] != endpoint 21 33 22 34 pkey = pkey_for_user(did) 23 35
+43 -19
app/server.rb
··· 23 23 content_type :json 24 24 [status, JSON.generate({ error: name, message: message })] 25 25 end 26 + 27 + def get_user_did 28 + if settings.development? 29 + if did = params[:user] 30 + return did 31 + else 32 + halt json_error('AuthMissing', 'Missing "user" parameter', status: 401) 33 + end 34 + else 35 + auth_header = env['HTTP_AUTHORIZATION'] 36 + if auth_header.to_s.strip.empty? 37 + halt json_error('AuthMissing', 'Missing authentication header', status: 401) 38 + end 39 + 40 + begin 41 + did = @authenticator.decode_user_from_jwt(auth_header, 'blue.feeds.lycan.searchPosts') 42 + rescue StandardError => e 43 + halt json_error('InvalidToken', e.message, status: 401) 44 + end 45 + 46 + if did 47 + return did 48 + else 49 + halt json_error('InvalidToken', "Invalid JWT token", status: 401) 50 + end 51 + end 52 + end 53 + 54 + def load_user 55 + did = get_user_did 56 + 57 + if user = User.find_by(did: did) 58 + return user 59 + else 60 + halt json_error('AccountNotFound', 'Account not found', status: 401) 61 + end 62 + end 26 63 end 27 64 28 65 before do ··· 32 69 get '/xrpc/blue.feeds.lycan.searchPosts' do 33 70 headers['access-control-allow-origin'] = '*' 34 71 35 - if settings.development? 36 - user = User.find_by(did: params[:user]) 37 - return json_error('UserNotFound', 'Missing "user" parameter') if user.nil? 38 - else 39 - begin 40 - auth_header = env['HTTP_AUTHORIZATION'] 41 - did = @authenticator.decode_user_from_jwt(auth_header, 'blue.feeds.lycan.searchPosts') 42 - rescue StandardError => e 43 - p e 44 - end 45 - 46 - user = did && User.find_by(did: did) 47 - return json_error('UserNotFound', 'Missing authentication header') if user.nil? 48 - end 72 + @user = load_user 49 73 50 74 if params[:query].to_s.strip.empty? 51 75 return json_error('MissingParameter', 'Missing "query" parameter') ··· 58 82 query = QueryParser.new(params[:query]) 59 83 60 84 collection = case params[:collection] 61 - when 'likes' then user.likes 62 - when 'pins' then user.pins 63 - when 'quotes' then user.quotes 64 - when 'reposts' then user.reposts 85 + when 'likes' then @user.likes 86 + when 'pins' then @user.pins 87 + when 'quotes' then @user.quotes 88 + when 'reposts' then @user.reposts 65 89 else return json_error('InvalidParameter', 'Invalid search collection') 66 90 end 67 91 ··· 80 104 81 105 post_uris = case params[:collection] 82 106 when 'quotes' 83 - items.map { |x| "at://#{user.did}/app.bsky.feed.post/#{x.rkey}" } 107 + items.map { |x| "at://#{@user.did}/app.bsky.feed.post/#{x.rkey}" } 84 108 else 85 109 items.map(&:post).map(&:at_uri) 86 110 end