Skip to content

Prevent login! failure blocks from masking InvalidCredentials#440

Open
willnet wants to merge 3 commits into
mainfrom
codex/login-bang-success-block
Open

Prevent login! failure blocks from masking InvalidCredentials#440
willnet wants to merge 3 commits into
mainfrom
codex/login-bang-success-block

Conversation

@willnet

@willnet willnet commented Jul 1, 2026

Copy link
Copy Markdown
Member

This PR fixes a bug that I found while trying to remove the mock in #438.

login! is documented to raise Sorcery::InvalidCredentials when authentication fails. When authenticate yields a failure reason, forwarding the caller's block to login lets controller code in the failure branch run before login! can raise, so errors such as a missing template can replace InvalidCredentials.

Call login without forwarding the block, then yield to the caller only after a successful authentication. This keeps login's failure-reason block behavior intact while preserving login!'s raise-on-failure contract.

Please ensure your pull request includes the following:

  • Description of changes
  • Changes have related RSpec tests that ensure functionality does not break

login! is documented to raise Sorcery::InvalidCredentials when authentication fails. When authenticate yields a failure reason, forwarding the caller's block to login lets controller code in the failure branch run before login! can raise, so errors such as a missing template can replace InvalidCredentials.

Call login without forwarding the block, then yield to the caller only after a successful authentication. This keeps login's failure-reason block behavior intact while preserving login!'s raise-on-failure contract.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts Sorcery::Controller#login! to preserve its documented contract of raising Sorcery::InvalidCredentials on authentication failure, even when the underlying authenticate implementation yields a failure reason (which previously allowed caller-provided blocks to run and potentially raise other errors first).

Changes:

  • Update login! to call login without forwarding the caller’s block, then yield only after a successful authentication.
  • Update the controller spec to stub User.authenticate using a yielded failure reason for the #login! failure case.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
spec/controllers/sorcery_controller_spec.rb Updates a failure stub to simulate authenticate yielding a failure reason.
lib/sorcery/controller.rb Prevents login! from executing caller blocks on authentication failure; yields only after success.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/sorcery/controller.rb Outdated
Comment on lines +62 to +67
def login!(*credentials)
user = login(*credentials)

raise Sorcery::InvalidCredentials if user.nil?

user
block_given? ? yield(user, nil) : user

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add/adjust a spec for #login! with block where User.authenticate yields (user, :invalid_password) and assert that the request raises Sorcery::InvalidCredentials

The spec updated in this PR is the one you're referring to.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think here there is still an omission. user was always returned previously, now it's only returned if a block wasn't given. Someone might be using a block and then also persisting the return value of login!.

Comment thread lib/sorcery/controller.rb Outdated
Comment on lines +62 to +67
def login!(*credentials)
user = login(*credentials)

raise Sorcery::InvalidCredentials if user.nil?

user
block_given? ? yield(user, nil) : user

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think here there is still an omission. user was always returned previously, now it's only returned if a block wasn't given. Someone might be using a block and then also persisting the return value of login!.

context 'when fails' do
before do
allow(User).to receive(:authenticate).with('bla@example.com', 'opensesame!').and_return(nil)
allow(User).to receive(:authenticate).with('bla@example.com', 'opensesame!').and_yield(user, :invalid_password)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't really matter, but this would be more correct if it yielded nil as the second parameter since that's what the real method does. You could probably add .and_return(user) to the end also. None of these paths are even followed in the failure case though. You could potentially have neither and_yield and and_return and it would be fine?

You could add some more tests that test that when you rescue the error, that the user is yielded along with a nil second value, and that the user is returned after the block is executed? And a test where there is no block passed.

willnet added 2 commits July 8, 2026 19:24
Keep yielding to a login! success block for compatibility, but do not let the block return value replace the authenticated user returned by login!.
Move the yielded failure regression coverage onto the block form of login!, where controller failure handling can otherwise mask Sorcery::InvalidCredentials.

Also assert that the success block form still assigns the authenticated user, matching login!'s user-returning contract.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants