Skip to content

Commit

Permalink
Add after_session_confirm hook (#237)
Browse files Browse the repository at this point in the history
  • Loading branch information
wilburhimself authored Oct 7, 2024
1 parent 061599b commit d4df0b3
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 0 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ Passwordless.configure do |config|
config.sign_out_redirect_path = '/' # After a user signs out

config.paranoid = false # Display email sent notice even when the resource is not found.

config.after_session_confirm = ->(request, session) {} # Called after a session is confirmed.
end
```

Expand All @@ -264,6 +266,20 @@ Passwordless.configure do |config|
end
```

## After Session Confirm Hook

An `after_session_confirm` hook is called after a successful session confirmation – in other words: after a user signs in successfully.

```ruby
Passwordless.configure do |config|
config.after_session_confirm = ->(session, request) {
user = session.authenticatable
user.update!(
email_verified: true.
last_login_ip: request.remote_ip
)
}
end
### Token generation

By default Passwordless generates short, 6-digit, alpha numeric tokens. You can change the generator using `Passwordless.config.token_generator` to something else that responds to `call(session)` eg.:
Expand Down
7 changes: 7 additions & 0 deletions app/controllers/passwordless/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ def artificially_slow_down_brute_force_attacks(token)
def authenticate_and_sign_in(session, token)
if session.authenticate(token)
sign_in(session)
call_after_session_confirm(session, request)
redirect_to(
passwordless_success_redirect_path(session.authenticatable),
status: :see_other,
Expand Down Expand Up @@ -188,6 +189,12 @@ def call_or_return(value, *args)
end
end

def call_after_session_confirm(session, request)
return unless Passwordless.config.after_session_confirm.respond_to?(:call)

Passwordless.config.after_session_confirm.call(session, request)
end

def find_authenticatable
if authenticatable_class.respond_to?(:fetch_resource_for_passwordless)
authenticatable_class.fetch_resource_for_passwordless(normalized_email_param)
Expand Down
6 changes: 6 additions & 0 deletions lib/passwordless/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ class Configuration

option :paranoid, default: false

option(
:after_session_confirm,
default: lambda do |_session, _request|
end
)

def initialize
set_defaults!
end
Expand Down
38 changes: 38 additions & 0 deletions test/controllers/passwordless/sessions_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,44 @@ class << User
assert_nil pwless_session(User)
end

test("PATCH /:passwordless_for/sign_in/:id -> after_session_confirm with request object") do
user = create_user(email: "[email protected]")
passwordless_session = create_pwless_session(authenticatable: user, token: "valid_token")

confirm_called = false

with_config(after_session_confirm: ->(session, request) {
confirm_called = true
assert_equal user, session.authenticatable
assert_kind_of ActionDispatch::Request, request
}) do
patch(
"/users/sign_in/#{passwordless_session.identifier}",
params: {passwordless: {token: "valid_token"}}
)
end

assert_equal 303, status
assert confirm_called, "after_session_confirm hook was not called"
end

test("PATCH /:passwordless_for/sign_in/:id -> after_session_confirm not called on invalid token") do
user = create_user(email: "[email protected]")
passwordless_session = create_pwless_session(authenticatable: user, token: "valid_token")

confirm_called = false

with_config(after_session_confirm: ->(_) { confirm_called = true }) do
patch(
"/users/sign_in/#{passwordless_session.identifier}",
params: {passwordless: {token: "invalid_token"}}
)
end

assert_equal 403, status
assert_not confirm_called, "after_session_confirm hook was called with invalid token"
end

test("DELETE /:passwordless_for/sign_out") do
user = User.create(email: "a@a")
passwordless_session = create_pwless_session(authenticatable: user, token: "hi")
Expand Down

0 comments on commit d4df0b3

Please sign in to comment.