Skip to content

Commit

Permalink
Allow overriding for finding record during session creation (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
avinoth authored and mikker committed Jan 4, 2019
1 parent b0fc343 commit f0f3a9d
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 15 deletions.
39 changes: 28 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Add authentication to your Rails app without all the icky-ness of passwords.
* [Usage](#usage)
* [Getting the current user, restricting access, the usual](#getting-the-current-user-restricting-access-the-usual)
* [Providing your own templates](#providing-your-own-templates)
* [Overrides](#overrides)
* [Registering new users](#registering-new-users)
* [Generating tokens](#generating-tokens)
* [Redirecting back after sign-in](#redirecting-back-after-sign-in)
Expand Down Expand Up @@ -51,7 +52,7 @@ Then specify which field on your `User` record is the email field with:
```ruby
class User < ApplicationRecord
validates :email, presence: true, uniqueness: { case_sensitive: false }

passwordless_with :email # <-- here!
end
```
Expand All @@ -73,13 +74,13 @@ Passwordless doesn't give you `current_user` automatically -- it's dead easy to
```ruby
class ApplicationController < ActionController::Base
include Passwordless::ControllerHelpers # <-- This!

# ...

helper_method :current_user

private

def current_user
@current_user ||= authenticate_by_cookie(User)
end
Expand All @@ -96,7 +97,7 @@ Et voilà:
```ruby
class VerySecretThingsController < ApplicationController
before_action :require_user!

def index
@things = current_user.very_secret_things
end
Expand All @@ -118,6 +119,22 @@ app/views/passwordless/mailer/magic_link.text.erb

See [the bundled views](https://github.com/mikker/passwordless/tree/master/app/views/passwordless).

### Overrides

By default `passwordless` uses the `passwordless_with` column you specify in the model to case insensitively fetch the resource during authentication. You can override this and provide your own customer fetcher by defining a class method `fetch_resource_for_passwordless` in your passwordless model. The method will be supplied with the downcased email and should return an `ActiveRecord` instance of the model.

Example time:

Let's say we would like to fetch the record and if it doesn't exist, create automatically.

```ruby
class User < ApplicationRecord
def self.fetch_resource_for_passwordless(email)
find_or_create_by(email: email)
end
end
```

### Registering new users

Because your `User` record is like any other record, you create one like you normally would. Passwordless provides a helper method you can use to sign in the created user after it is saved like so:
Expand All @@ -129,15 +146,15 @@ class UsersController < ApplicationController

def create
@user = User.new user_params

if @user.save
sign_in @user # <-- And this!
redirect_to @user, flash: {notice: 'Welcome!'}
else
render :new
end
end

# ...
end
```
Expand All @@ -161,9 +178,9 @@ By default Passwordless will redirect back to where the user wanted to go **if**
```ruby
class ApplicationController < ActionController::Base
include Passwordless::ControllerHelpers # <-- Probably already have this!

# ...

def require_user!
return if current_user
save_passwordless_redirect_location!(User) # <-- here we go!
Expand All @@ -181,7 +198,7 @@ By default, Passwordless uses the resource name given to `passwordless_for` to g
```ruby
passwordless_for :users
# <%= users.sign_in_path %> # => /users/sign_in

passwordless_for :users, at: '/', as: :auth
# <%= auth.sign_in_path %> # => /sign_in
```
Expand Down
12 changes: 9 additions & 3 deletions app/controllers/passwordless/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,15 @@ def email_field
end

def find_authenticatable
authenticatable_class.where(
"lower(#{email_field}) = ?", params[:passwordless][email_field].downcase
).first
email = params[:passwordless][email_field].downcase

if authenticatable_class.respond_to?(:fetch_resource_for_passwordless)
authenticatable_class.fetch_resource_for_passwordless(email)
else
authenticatable_class.where(
"lower(#{email_field}) = ?", params[:passwordless][email_field].downcase
).first
end
end

def find_session
Expand Down
22 changes: 21 additions & 1 deletion test/controllers/passwordless/sessions_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,33 @@ def create_session_for(user)
assert_equal 200, status

post '/users/sign_in',
params: { passwordless: { email: 'something_em@ilish' } },
params: { passwordless: { email: 'invalidemail' } },
headers: { 'User-Agent': 'an actual monkey' }
assert_equal 200, status

assert_equal 0, ActionMailer::Base.deliveries.size
end

test 'requesting a magic link with overridden fetch method' do
def User.fetch_resource_for_passwordless(email)
User.find_or_create_by(email: email)
end

get '/users/sign_in'
assert_equal 200, status

post '/users/sign_in',
params: { passwordless: { email: 'overriden_email@example' } },
headers: { 'User-Agent': 'an actual monkey' }
assert_equal 200, status

assert_equal 1, ActionMailer::Base.deliveries.size

class << User
remove_method :fetch_resource_for_passwordless
end
end

test 'signing in via a token' do
user = User.create email: 'a@a'
session = create_session_for user
Expand Down

0 comments on commit f0f3a9d

Please sign in to comment.