Skip to content

Commit 2a60821

Browse files
committed
Add #set_unusable_password helper method to user model
1 parent cd75243 commit 2a60821

File tree

3 files changed

+31
-0
lines changed

3 files changed

+31
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## 0.1.1 (UNDER DEVELOPMENT)
44

55
* Ensure `MartenAuth::User#check_password` returns `false` in case the password is not a correctly encoded value
6+
* Add a `MartenAuth::User#set_unusable_password` method for situations where it's necessary to assign a non-usable password to a user
67

78
## 0.1.0 (2023-02-11)
89

spec/marten_auth/models/user_spec.cr

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,23 @@ describe MartenAuth::User do
6161
end
6262
end
6363

64+
describe "#set_unusable_password" do
65+
it "assigns a unique and invalid hash to the user password field" do
66+
user = User.new(email: "test@example.com", password: nil)
67+
68+
user.set_unusable_password
69+
70+
user.password!.size.should eq 41
71+
user.password!.starts_with?('!').should be_true
72+
user.check_password(user.password!).should be_false
73+
74+
old_unusable_password = user.password
75+
76+
user.set_unusable_password
77+
user.password.should_not eq old_unusable_password
78+
end
79+
end
80+
6481
describe "#session_auth_hash" do
6582
it "it returns the expected HMAC computed from the password" do
6683
user = create_user(email: "test@example.com", password: "insecure")

src/marten_auth/models/user.cr

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ module MartenAuth
4242
self.password = Crypto::Bcrypt::Password.create(raw_password).to_s
4343
end
4444

45+
# Allows to assign a non-usable password to the user.
46+
#
47+
# The assigned value is not a valid hash and will never be usable by the user.
48+
def set_unusable_password : Nil
49+
self.password = String.build do |s|
50+
s << UNUSABLE_PASSWORD_PREFIX
51+
s << Random::Secure.random_bytes((UNUSABLE_PASSWORD_SUFFIX_LENGTH / 2).to_i).hexstring
52+
end
53+
end
54+
4555
# Returns the authentication hash (HMAC computed from the password) that should be embedded in sessions.
4656
def session_auth_hash : String
4757
key_salt = "MartenAuth::User#session_auth_hash"
@@ -51,5 +61,8 @@ module MartenAuth
5161

5262
OpenSSL::HMAC.hexdigest(OpenSSL::Algorithm::SHA256, key, password!)
5363
end
64+
65+
private UNUSABLE_PASSWORD_PREFIX = "!" # This ensures that the password will never be a valid encoded hash.
66+
private UNUSABLE_PASSWORD_SUFFIX_LENGTH = 40
5467
end
5568
end

0 commit comments

Comments
 (0)