Tworzę autentykację poprzez API opartą na BCrypt. Z weryfikacją hasła nie było żadnych problemów, ale chciałbym api keye trzymać w bazie zaszyfrowane, tak jak hasła. I tu zaczynają się schody. W przeglądarce trzymam surowe api keye. Po wysłaniu requesta z api keyem chciałbym go zahaszować i po tym haszu znaleźć użytkownika. Problem w tym, że kolejne wykonanie tej samej funkcji haszującej na tym samym stringu daje inny hasz. Sprawdziłem jak to wygląda dla funkcji authenticate w BCrypt i odkryłem coś naprawdę dziwnego.
W skrócie, mimo że wyświetlany jest jako zwykły string (co zresztą sprawdziłeś, bo to zależy wyłącznie od tego, co zwraca metoda inspect), nie jest to String, a obiekt klasy BCrypt::Password, która dziedziczy po klasie String.
Czegoś takiego się spodziewałem. Bardziej mnie dziwi jednak to, że porównanie takiego obiektu z czystym stringiem daje true. Jak dotrzeć do kodu, który to implementuje?
Polecam także używać narzędzie pry. W momencie gdy sprawdziłeś, że wyrażenie a == 'abc' zwraca true (co oczywiście było zaskoczeniem) powinieneś sprawdzić jakiego typu obiekt masz po lewej stronie (a.class) a także co robi metoda == wywołana na zmiennej a. I to jest najprościej zrobić w pry:
$ pry
[1] pry(main)> require "bcrypt"
=> true
[2] pry(main)> a = BCrypt::Password.create('abc')
=> "$2a$10$uara64IQDwbnjuBSBLPJu.0PRmYaJXlnkmLKiCj9hDqUu2pPB6NTu"
[3] pry(main)> a == 'abc'
=> true
[4] pry(main)> $ a.==
From: /Users/radarek/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/bcrypt-3.1.11/lib/bcrypt/password.rb @ line 65:
Owner: BCrypt::Password
Visibility: public
Number of lines: 3
def ==(secret)
super(BCrypt::Engine.hash_secret(secret, @salt))
end