Code Monkey home page Code Monkey logo

rails_jwt_auth's Introduction

RailsJwtAuth

Gem Version Build Status

Rails-API authentication solution based on JWT and inspired by Devise.

This is documentation for version 2.x. If you are using 1.x version use this link

Version 2.x introduces incompatible API changes.

Table of Contents

Installation

Add this line to your application's Gemfile:

gem 'rails_jwt_auth'

And then execute:

$ bundle

Or install it yourself as:

$ gem install rails_jwt_auth

Finally execute:

rails g rails_jwt_auth:install

Only for ActiveRecord, generate migrations:

rails g rails_jwt_auth:migrate

Configuration

You can edit configuration options into config/initializers/rails_jwt_auth.rb file created by generator.

Option Default value Description
model_name 'User' Authentication model name
auth_field_name 'email' Field used to authenticate user with password
email_auth_field 'email' Field used to send emails
email_regex URI::MailTo::EMAIL_REGEXP Regex used to validate email input on requests like reset password
downcase_auth_field false Apply downcase to auth field when save user and when init session
jwt_expiration_time 7.days Tokens expiration time
jwt_issuer 'RailsJwtAuth' The "iss" (issuer) claim identifies the principal that issued the JWT
simultaneous_sessions 2 Number of simultaneous sessions for an user. Set 0 to disable sessions
mailer_name 'RailsJwtAuth::Mailer' Mailer class name (allow customize mailer)
mailer_sender [email protected] E-mail address which will be shown in RailsJwtAuth::Mailer
send_email_change_requested_notification true Notify original email when change is requested (unconfirmed)
send_password_changed_notification true Notify email when password changes
confirmation_expiration_time 1.day Confirmation token expiration time
reset_password_expiration_time 1.day Confirmation token expiration time
deliver_later false Uses deliver_later method to send emails
invitation_expiration_time 2.days Time an invitation is valid and can be accepted
lock_strategy :none Strategy to be used to lock an account: :none or :failed_attempts
unlock_strategy :time Strategy to use when unlocking accounts: :time, :email or :both
unlock_in 60.minutes Interval to unlock an account if unlock_strategy is :time
reset_attempts_in 60.minutes Interval after which to reset failed attempts counter of an account
maximum_attempts 3 Number of failed login attempts before locking an account
confirm_email_url nil Your web url where emai link redirects with confirmation token
reset_password_url nil Your web url where emai link redirects with reset password token
accept_invitation_url nil Your web url where emai link redirects with invitation token
unlock_account_url nil Your web url where emai link redirects with unlock token
avoid_email_errors true Avoid returns email errors to avoid giving clue to an attacker

Modules

It's composed of 6 modules:

Module Description
Authenticable Hashes and stores a password in the database to validate the authenticity of a user while signing in
Confirmable Sends emails with confirmation instructions and verifies whether an account is already confirmed during sign in
Recoverable Resets the user password and sends reset instructions
Trackable Tracks sign in and request timestamps and IP address
Invitable Allows you to invite an user to your application sending an invitation mail
Lockable Locks the user after a specified number of failed sign in attempts

ORMs support

RailsJwtAuth support both Mongoid and ActiveRecord.

For next examples auth_field_name and email_field_name are configured to use the field email.

ActiveRecord

# app/models/user.rb
class User < ApplicationRecord
  include RailsJwtAuth::Authenticatable
  include RailsJwtAuth::Confirmable
  include RailsJwtAuth::Recoverable
  include RailsJwtAuth::Trackable
  include RailsJwtAuth::Invitable
  include RailsJwtAuth::Lockable

  validates :email, presence: true,
                    uniqueness: true,
                    format: URI::MailTo::EMAIL_REGEXP
end

Ensure you have executed migrate task: rails g rails_jwt_auth:migrate and you have uncomented all modules fields into generated migration file.

Mongoid

class User
  include Mongoid::Document
  include RailsJwtAuth::Authenticatable
  include RailsJwtAuth::Confirmable
  include RailsJwtAuth::Recoverable
  include RailsJwtAuth::Trackable
  include RailsJwtAuth::Invitable
  include RailsJwtAuth::Lockable

  field :email, type: String

  validates :email, presence: true,
                    uniqueness: true,
                    format: URI::MailTo::EMAIL_REGEXP
end

Controller helpers

RailsJwtAuth will create some helpers to use inside your controllers.

To use this helpers we need to include AuthenticableHelper into ApplicationController:

# app/controllers/application_controller.rb
class ApplicationController < ActionController::API
  include RailsJwtAuth::AuthenticableHelper
end
  • authenticate!

    Authenticate your controllers:

    class MyController < ApplicationController
      before_action :authenticate!
    end

    This helper expect that token has been into AUTHORIZATION header. Raises RailsJwtAuth::NotAuthorized exception when it fails.

  • authenticate

    Authenticate your controllers:

    class MyController < ApplicationController
      before_action :authenticate
    end

    This helper is like authenticate! but it not raises exception

  • current_user

    Return current signed-in user.

  • jwt_payload

    Return current jwt payload.

  • signed_in?

    Verify if a user is signed in.

Default Controllers API

Prefix Verb URI Pattern Controller#Action
session DELETE /session(.:format) rails_jwt_auth/sessions#destroy
POST /session(.:format) rails_jwt_auth/sessions#create
registration POST /registration(.:format) rails_jwt_auth/registrations#create
profile GET /profile(.:format) rails_jwt_auth/profiles#show
mail_profile PUT /profile/email(.:format) rails_jwt_auth/profiles#email
password_profile PUT /profile/password(.:format) rails_jwt_auth/profiles#password
PUT /profile(.:format) rails_jwt_auth/profiles#update
confirmations POST /confirmations(.:format) rails_jwt_auth/confirmations#create
confirmation PUT /confirmations/:id(.:format) rails_jwt_auth/confirmations#update
reset_passwords POST /reset_passwords(.:format) rails_jwt_auth/reset_passwords#create
reset_password GET /reset_passwords/:id(.:format) rails_jwt_auth/reset_passwords#show
PUT /reset_passwords/:id(.:format) rails_jwt_auth/reset_passwords#update
invitations POST /invitations(.:format) rails_jwt_auth/invitations#create
invitation GET /invitations/:id(.:format) rails_jwt_auth/invitations#show
PUT /invitations/:id(.:format) rails_jwt_auth/invitations#update
unlock_account PUT /unlock_accounts/:id(.:format) rails_jwt_auth/unlock_accounts#update

Session

Session api is defined by RailsJwtAuth::SessionsController.

  1. Get session token:
{
  url: host/session,
  method: POST,
  data: {
    session: {
      email: '[email protected]',
      password: '12345678'
    }
  }
}
  1. Delete session
{
  url: host/session,
  method: DELETE,
  headers: { 'Authorization': 'Bearer auth_token'}
}

Registration

Registration api is defined by RailsJwtAuth::RegistrationsController.

  1. Register user:
{
  url: host/registration,
  method: POST,
  data: {
    user: {
      email: '[email protected]',
      password: 'xxxx'
    }
  }
}

Profile

Profile api let you get/update your user info and is defined by RailsJwtAuth::ProfilesController.

  1. Get user info:
{
  url: host/profile,
  method: GET,
  headers: { 'Authorization': 'Bearer auth_token'}
}
  1. Update user info:
{
  url: host/profile,
  method: PUT,
  data: {
    profile: {
      name: 'new_name',
    }
  },
  headers: { 'Authorization': 'Bearer auth_token'}
}
  1. Update user password:
{
  url: host/profile/password,
  method: PUT,
  data: {
    profile: {
      current_password: 'xxxx',
      password: 'yyyy',
    }
  },
  headers: { 'Authorization': 'Bearer auth_token'}
}
  1. Update user email (needs confirmation module):
{
  url: host/profile/email,
  method: PUT,
  data: {
    profile: {
      email: '[email protected]',
      password: 'xxxx', # email change is protected by password
    }
  },
  headers: { 'Authorization': 'Bearer auth_token'}
}

Confirmation

Confirmation api is defined by RailsJwtAuth::ConfirmationsController.

It is necessary to set a value for confirmations_url option into config/initializers/rails_jwt_auth.rb.

  1. Confirm user:
{
  url: host/confirmations/:token,
  method: PUT
  data: {}
}
  1. Create confirmation (resend confirmation email):
{
  url: host/confirmations,
  method: POST,
  data: {
    confirmation: {
      email: '[email protected]'
    }
  }
}

Password

Reset password api is defined by RailsJwtAuth::ResetPasswordsController.

  1. Send reset password email (init reset password process):
{
  url: host/reset_passwords,
  method: POST,
  data: {
    reset_password: {
      email: '[email protected]'
    }
  }
}
  1. Check token validation:

Used to verify a token and show an alert in your website before the new password is set.

{
  url: host/reset_passwords/:token,
  method: GET
}
  1. Update password:
{
  url: host/reset_passwords/:token,
  method: PUT,
  data: {
    reset_password: {
      password: '1234',
      password_confirmation: '1234'
    }
  }
}

Invitations

Invitations api is provided by RailsJwtAuth::InvitationsController.

  1. Create an invitation and send email:
{
  url: host/invitations,
  method: POST,
  data: {
    invitation: {
      email: '[email protected]',
      // More fields of your user
    }
  }
}
  1. Check token validation:

Used to verify token and show an alert in your web before invitation data is completed.

{
  url: host/invitations/:token,
  method: GET
}
  1. Accept an invitation:
{
  url: host/invitations/:invitation_token,
  method: PUT,
  data: {
    invitation: {
      password: '1234',
      password_confirmation: '1234'
    }
  }
}

Note: To add more fields, see "Custom strong parameters" below.

Unlocks

Unlock api is provided by RailsJwtAuth::UnlocksController.

  1. Unlock user:
{
  url: host/unlock_accounts/:unlock_token,
  method: PUT,
  data: {}
}

Customize

RailsJwtAuth offers an easy way to customize certain parts.

Custom controllers

You can overwrite RailsJwtAuth controllers to edit actions, responses, permitted parameters...

For example, if we want to call custom method when user is created we need to create new registration controller inherited from default controller:

# app/controllers/registrations_controller.rb
class RegistrationsController < RailsJwtAuth::RegistrationsController
  ...

  def create
    user = RailsJwtAuth.model.new(create_params)
    user.do_something_custom
    ...
  end

  ...
end

And edit route resource to use it:

# config/routes.rb
resource :registration, controller: 'registrations', only: [:create, :update, :destroy]

Custom payload

If you need edit default payload used to generate jwt you can overwrite the method to_token_payload into your User class:

class User < ApplicationRecord
  include RailsJwtAuth::Authenticatable
  ...

  def to_token_payload(request)
    {
      auth_token: auth_tokens.last,
      # add here your custom info
    }
  end
end

Custom responses

You can overwrite RailsJwtAuth::RenderHelper to customize controllers responses without need to overwrite each controller.

Example:

# app/controllers/concerns/rails_jwt_auth/render_helper.rb

module RailsJwtAuth
  module RenderHelper
    private

    def render_session(jwt, user)
      # add custom field to session response
      render json: {session: {jwt: jwt, my_custom_field: user.custom_field}}, status: 201
    end

  ...
end

Custom strong parameters

You can overwrite RailsJwtAuth::ParamsHelper to customize controllers strong parameters without need to overwrite each controller.

Example:

# app/controllers/concerns/rails_jwt_auth/params_helper.rb

module RailsJwtAuth
  module ParamsHelper
    private

    def registration_create_params
      # change root to :data
      params.require(:data).permit(:email, :password, :password_confirmation)
    end

  ...
end

Custom mailer

To use a custom mailer, create a class that extends RailsJwtAuth::Mailer, like this:

class CustomMailer < RailsJwtAuth::Mailer
  def confirmation_instructions(user)
    # set your custom code here

    super
  end
end

Then, in your config/initializers/rails_jwt_auth.rb, set config.mailer to "CustomMailer".

If you only need to customize templates, overwrite files in 'app/views/rails_jwt_auth/mailer'

Testing (rspec)

Require the RailsJwtAuth::Spec::Helpers helper module in rails_helper.rb.

require 'rails_jwt_auth/spec_helpers'
...
RSpec.configure do |config|
  ...
  config.include RailsJwtAuth::SpecHelpers, type: :controller
end

And then we can just call sign_in(user) to sign in as a user:

describe ExampleController
  it 'blocks unauthenticated access' do
    expect { get :index }.to raise_error(RailsJwtAuth::NotAuthorized)
  end

  it 'allows authenticated access' do
    sign_in user
    get :index
    expect(response).to be_success
  end
end

Locales

Copy config/locales/en.yml into your project config/locales folder and edit it.

License

The gem is available as open source under the terms of the MIT License.

rails_jwt_auth's People

Contributors

blanecordes avatar dependabot[bot] avatar ggomagundan avatar mfloresnss avatar pacop avatar patosoft avatar rjurado01 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

rails_jwt_auth's Issues

No destroy action for RailsJwtAuth::RegistrationsController

I think I have done everything properly. The same "/registration" POST (for creating a new user) works perfectly fine. On the source code it seems like the method destroy is not written. Is it supposed to be written? Cause I could give it a try and add it myself :) BTW, this gem is awesome, its soo handy and beautifully written. Keep it up!

{ "status": 404, "error": "Not Found", "exception": "#<AbstractController::ActionNotFound: The action 'destroy' could not be found for RailsJwtAuth::RegistrationsController>", "traces": { "Application Trace": [], "Framework Trace": [ { "id": 0, "trace": "actionpack (5.1.6) lib/abstract_controller/base.rb:119:in process'"
},
{
"id": 1,
"trace": "actionpack (5.1.6) lib/action_controller/metal.rb:189:in dispatch'" }, { "id": 2, "trace": "actionpack (5.1.6) lib/action_controller/metal.rb:253:in dispatch'"
},
{
"id": 3,
"trace": "actionpack (5.1.6) lib/action_dispatch/routing/route_set.rb:49:in dispatch'" }, { "id": 4, "trace": "actionpack (5.1.6) lib/action_dispatch/routing/route_set.rb:31:in serve'"
},
{
"id": 5,
"trace": "actionpack (5.1.6) lib/action_dispatch/journey/router.rb:50:in block in serve'" }, { "id": 6, "trace": "actionpack (5.1.6) lib/action_dispatch/journey/router.rb:33:in each'"
},
{
"id": 7,
"trace": "actionpack (5.1.6) lib/action_dispatch/journey/router.rb:33:in serve'" }, { "id": 8, "trace": "actionpack (5.1.6) lib/action_dispatch/routing/route_set.rb:844:in call'"
},
{
"id": 9,
"trace": "rack (2.0.5) lib/rack/etag.rb:25:in call'" }, { "id": 10, "trace": "rack (2.0.5) lib/rack/conditional_get.rb:38:in call'"
},
{
"id": 11,
"trace": "rack (2.0.5) lib/rack/head.rb:12:in call'" }, { "id": 12, "trace": "activerecord (5.1.6) lib/active_record/migration.rb:556:in call'"
},
{
"id": 13,
"trace": "warden (1.2.7) lib/warden/manager.rb:36:in block in call'" }, { "id": 14, "trace": "warden (1.2.7) lib/warden/manager.rb:35:in catch'"
},
{
"id": 15,
"trace": "warden (1.2.7) lib/warden/manager.rb:35:in call'" }, { "id": 16, "trace": "actionpack (5.1.6) lib/action_dispatch/middleware/callbacks.rb:26:in block in call'"
},
{
"id": 17,
"trace": "activesupport (5.1.6) lib/active_support/callbacks.rb:97:in run_callbacks'" }, { "id": 18, "trace": "actionpack (5.1.6) lib/action_dispatch/middleware/callbacks.rb:24:in call'"
},
{
"id": 19,
"trace": "actionpack (5.1.6) lib/action_dispatch/middleware/executor.rb:12:in call'" }, { "id": 20, "trace": "actionpack (5.1.6) lib/action_dispatch/middleware/debug_exceptions.rb:59:in call'"
},
{
"id": 21,
"trace": "actionpack (5.1.6) lib/action_dispatch/middleware/show_exceptions.rb:31:in call'" }, { "id": 22, "trace": "railties (5.1.6) lib/rails/rack/logger.rb:36:in call_app'"
},
{
"id": 23,
"trace": "railties (5.1.6) lib/rails/rack/logger.rb:24:in block in call'" }, { "id": 24, "trace": "activesupport (5.1.6) lib/active_support/tagged_logging.rb:69:in block in tagged'"
},
{
"id": 25,
"trace": "activesupport (5.1.6) lib/active_support/tagged_logging.rb:26:in tagged'" }, { "id": 26, "trace": "activesupport (5.1.6) lib/active_support/tagged_logging.rb:69:in tagged'"
},
{
"id": 27,
"trace": "railties (5.1.6) lib/rails/rack/logger.rb:24:in call'" }, { "id": 28, "trace": "actionpack (5.1.6) lib/action_dispatch/middleware/remote_ip.rb:79:in call'"
},
{
"id": 29,
"trace": "actionpack (5.1.6) lib/action_dispatch/middleware/request_id.rb:25:in call'" }, { "id": 30, "trace": "rack (2.0.5) lib/rack/runtime.rb:22:in call'"
},
{
"id": 31,
"trace": "activesupport (5.1.6) lib/active_support/cache/strategy/local_cache_middleware.rb:27:in call'" }, { "id": 32, "trace": "actionpack (5.1.6) lib/action_dispatch/middleware/executor.rb:12:in call'"
},
{
"id": 33,
"trace": "actionpack (5.1.6) lib/action_dispatch/middleware/static.rb:125:in call'" }, { "id": 34, "trace": "rack (2.0.5) lib/rack/sendfile.rb:111:in call'"
},
{
"id": 35,
"trace": "rack-cors (1.0.2) lib/rack/cors.rb:97:in call'" }, { "id": 36, "trace": "railties (5.1.6) lib/rails/engine.rb:522:in call'"
},
{
"id": 37,
"trace": "puma (3.11.4) lib/puma/configuration.rb:225:in call'" }, { "id": 38, "trace": "puma (3.11.4) lib/puma/server.rb:632:in handle_request'"
},
{
"id": 39,
"trace": "puma (3.11.4) lib/puma/server.rb:446:in process_client'" }, { "id": 40, "trace": "puma (3.11.4) lib/puma/server.rb:306:in block in run'"
},
{
"id": 41,
"trace": "puma (3.11.4) lib/puma/thread_pool.rb:120:in block in spawn_thread'" } ], "Full Trace": [ { "id": 0, "trace": "actionpack (5.1.6) lib/abstract_controller/base.rb:119:in process'"
},
{
"id": 1,
"trace": "actionpack (5.1.6) lib/action_controller/metal.rb:189:in dispatch'" }, { "id": 2, "trace": "actionpack (5.1.6) lib/action_controller/metal.rb:253:in dispatch'"
},
{
"id": 3,
"trace": "actionpack (5.1.6) lib/action_dispatch/routing/route_set.rb:49:in dispatch'" }, { "id": 4, "trace": "actionpack (5.1.6) lib/action_dispatch/routing/route_set.rb:31:in serve'"
},
{
"id": 5,
"trace": "actionpack (5.1.6) lib/action_dispatch/journey/router.rb:50:in block in serve'" }, { "id": 6, "trace": "actionpack (5.1.6) lib/action_dispatch/journey/router.rb:33:in each'"
},
{
"id": 7,
"trace": "actionpack (5.1.6) lib/action_dispatch/journey/router.rb:33:in serve'" }, { "id": 8, "trace": "actionpack (5.1.6) lib/action_dispatch/routing/route_set.rb:844:in call'"
},
{
"id": 9,
"trace": "rack (2.0.5) lib/rack/etag.rb:25:in call'" }, { "id": 10, "trace": "rack (2.0.5) lib/rack/conditional_get.rb:38:in call'"
},
{
"id": 11,
"trace": "rack (2.0.5) lib/rack/head.rb:12:in call'" }, { "id": 12, "trace": "activerecord (5.1.6) lib/active_record/migration.rb:556:in call'"
},
{
"id": 13,
"trace": "warden (1.2.7) lib/warden/manager.rb:36:in block in call'" }, { "id": 14, "trace": "warden (1.2.7) lib/warden/manager.rb:35:in catch'"
},
{
"id": 15,
"trace": "warden (1.2.7) lib/warden/manager.rb:35:in call'" }, { "id": 16, "trace": "actionpack (5.1.6) lib/action_dispatch/middleware/callbacks.rb:26:in block in call'"
},
{
"id": 17,
"trace": "activesupport (5.1.6) lib/active_support/callbacks.rb:97:in run_callbacks'" }, { "id": 18, "trace": "actionpack (5.1.6) lib/action_dispatch/middleware/callbacks.rb:24:in call'"
},
{
"id": 19,
"trace": "actionpack (5.1.6) lib/action_dispatch/middleware/executor.rb:12:in call'" }, { "id": 20, "trace": "actionpack (5.1.6) lib/action_dispatch/middleware/debug_exceptions.rb:59:in call'"
},
{
"id": 21,
"trace": "actionpack (5.1.6) lib/action_dispatch/middleware/show_exceptions.rb:31:in call'" }, { "id": 22, "trace": "railties (5.1.6) lib/rails/rack/logger.rb:36:in call_app'"
},
{
"id": 23,
"trace": "railties (5.1.6) lib/rails/rack/logger.rb:24:in block in call'" }, { "id": 24, "trace": "activesupport (5.1.6) lib/active_support/tagged_logging.rb:69:in block in tagged'"
},
{
"id": 25,
"trace": "activesupport (5.1.6) lib/active_support/tagged_logging.rb:26:in tagged'" }, { "id": 26, "trace": "activesupport (5.1.6) lib/active_support/tagged_logging.rb:69:in tagged'"
},
{
"id": 27,
"trace": "railties (5.1.6) lib/rails/rack/logger.rb:24:in call'" }, { "id": 28, "trace": "actionpack (5.1.6) lib/action_dispatch/middleware/remote_ip.rb:79:in call'"
},
{
"id": 29,
"trace": "actionpack (5.1.6) lib/action_dispatch/middleware/request_id.rb:25:in call'" }, { "id": 30, "trace": "rack (2.0.5) lib/rack/runtime.rb:22:in call'"
},
{
"id": 31,
"trace": "activesupport (5.1.6) lib/active_support/cache/strategy/local_cache_middleware.rb:27:in call'" }, { "id": 32, "trace": "actionpack (5.1.6) lib/action_dispatch/middleware/executor.rb:12:in call'"
},
{
"id": 33,
"trace": "actionpack (5.1.6) lib/action_dispatch/middleware/static.rb:125:in call'" }, { "id": 34, "trace": "rack (2.0.5) lib/rack/sendfile.rb:111:in call'"
},
{
"id": 35,
"trace": "rack-cors (1.0.2) lib/rack/cors.rb:97:in call'" }, { "id": 36, "trace": "railties (5.1.6) lib/rails/engine.rb:522:in call'"
},
{
"id": 37,
"trace": "puma (3.11.4) lib/puma/configuration.rb:225:in call'" }, { "id": 38, "trace": "puma (3.11.4) lib/puma/server.rb:632:in handle_request'"
},
{
"id": 39,
"trace": "puma (3.11.4) lib/puma/server.rb:446:in process_client'" }, { "id": 40, "trace": "puma (3.11.4) lib/puma/server.rb:306:in block in run'"
},
{
"id": 41,
"trace": "puma (3.11.4) lib/puma/thread_pool.rb:120:in block in spawn_thread'" } ] } }

Autogenerated initializer error

Hi, there is a small error in the autogenerated initializer after installing the gem:

# number of simultaneously sessions for an user
# config.simultaneously_sessions = 2

should be

# number of simultaneously sessions for an user
# config.simultaneous_sessions = 2  

Thank you for this amazing job!

EDIT:
The same is happening with
config.accept_invitation_url = 'http://frontend.com/accept_invitation'

should be:
config.invitation_url = 'http://frontend.com/accept_invitation'

Typo in README

I found a small typo in the README, wich delivers in a error later using the gem:

Under the Confirmated section, in the example migration to add to the database, it says:

...
t.datetime :confimed_at

and should be:

...
t.datetime :confirmed_at

Thanks again!

omniauth

Hello @rjurado01 ! thank you for the gem!
the omniauth support is already ok to use?

Forgot token = null y Confirmation token = null devuelve user

Cuando se va a activar una cuenta o a cambiar la contraseña de la misma, model.where(token: param[:token]).first devuelve un usuario si el token es '' o null.

Esto ocurre en los métodos update del controlador de password y de confirmation.

When changing email, there is no more any indication sent to old one

I have tried to change the user's email and it was successfully updated and confirmation instructions were sent to the newly updated email but I want a way to notify the old email also like "Your email was updated and confirmation instructions were sent to the new mail id........"

Little confused about DELETE /sessions/:id route (and other DELETE routes for that matter)

So the route is defined, from the generator, as:

DELETE /session/:id(.:format)

However, the documentation in the readme simply shows setting the auth header and there is no mention of setting the :id param in the path.

Delete session
{
url: host/session,
method: DELETE,
headers: { 'Authorization': 'Bearer auth_token'}
}

Not passing an :id results in a route not found error. Passing in ANY value for :id gets me past the route error, however, the value of the param does not appear to be used anywhere.

Can you explain why the param is necessary at all? Why is it not mentioned in the documentation?

Thanks.

signed_in? and current_user are useless

Hi, "signed_in?" is obviously false if you don't set the authenticate!. But if you do the authenticate! and the user is not logged in, the execution of the program will stop.

I need a way to see if a user is signed_in?, and show them something. If they are not signed_in? I want to show them something else in the same controller and action.

Kind regards,

Carlino

undefined method `authenticate!' for #<RailsJwtAuth::SessionsController:0x000000012ec68f98>

Add the below statement in sessions contoller, profiles controller (in whichever controller authentication is required)

  include AuthenticableHelper

While doing sign out, it checks authentication, at that time missed including authenticable helper

and In the documentation you have given the following URL for reset password

url: host/passwords/:token

but it must be

url: host/reset_passwords/:token

Confirmation link sent by email using get and no put in the url

When I register a new user and send an e-mail for confirmation, when I click on the link the message "No route matches [GET]" / confirmation "" appears, I saw that in the past action: show was used, but it was changed to: update ( : put).

As the link is generated by rails_jwt_auth I don't know how I could send a [PUT] "/ confirmation".

May help to solve this problem.
Thank you.

*** LOCAL GEMS ***
rails_jwt_auth (1.5.0)
Author: rjurado
Homepage: https://github.com/rjurado01/rails_jwt_auth
Rails jwt authentication.

Create user without password?

Do you have any suggestions for handling the case where we'd like to create users in our database (for something like progressive signup) without a password? Devise allowed you to bypass password verification, but looking at the code here it seems like that's not an easy option.

Thanks!

Testing raises uninitialized constant RailsJwtAuth::NotAuthorized

I am trying to test my controllers, but it doesn't seem to work. Am I doing something incorrect?

With rspec, when I generate a controller spec, it actually generates a request spec. Not sure if this matters.

In rails_helper.rb I have included:
require 'rails_jwt_auth/spec_helpers'

as well as in the config block:
config.include RailsJwtAuth::SpecHelpers, type: :controller

Here's my controller spec code:

require 'rails_helper'

RSpec.describe "Certificates", type: :request do
  it 'blocks unauthenticated access' do
    expect { get '/v1/certificates/mine' }.to raise_error(RailsJwtAuth::NotAuthorized)
  end

  it 'allows authenticated access' do
    user = create(:user)
    sign_in user
    get '/v1/certificates/mine'
    expect(response).to be_success
  end
end

Here's the spec output:

Failures:

  1) Certificates blocks unauthenticated access
     Failure/Error: expect { get '/v1/certificates/mine' }.to raise_error(RailsJwtAuth::NotAuthorized)

     NameError:
       uninitialized constant RailsJwtAuth::NotAuthorized
     # ./spec/requests/certificates_request_spec.rb:5:in `block (2 levels) in <top (required)>'

  2) Certificates allows authenticated access
     Failure/Error: sign_in user

     NoMethodError:
       undefined method `sign_in' for #<RSpec::ExampleGroups::Certificates:0x00007fad582aaac8>
     # ./spec/requests/certificates_request_spec.rb:10:in `block (2 levels) in <top (required)>'

Finished in 0.3141 seconds (files took 1.1 seconds to load)
2 examples, 2 failures

Failed examples:

rspec ./spec/requests/certificates_request_spec.rb:4 # Certificates blocks unauthenticated access
rspec ./spec/requests/certificates_request_spec.rb:8 # Certificates allows authenticated access

undefined render_204 method

Hello, same issue with the render_204 method.

Rails version : Rails 7.0.2
Rails Jwt Auth version : 2.0.3
Action : RailsJwtAuth::ResetPasswordsController#create

NameError (undefined local variable or method `render_204' for #RailsJwtAuth::ResetPasswordsController:0x0000000000ef10
Did you mean? render):

rails_jwt_auth (2.0.3) app/controllers/rails_jwt_auth/reset_passwords_controller.rb:30:in create' actionpack (7.0.2.3) lib/action_controller/metal/basic_implicit_render.rb:6:in send_action'
actionpack (7.0.2.3) lib/abstract_controller/base.rb:214:in process_action' actionpack (7.0.2.3) lib/action_controller/metal/rendering.rb:53:in process_action'
actionpack (7.0.2.3) lib/abstract_controller/callbacks.rb:234:in block in process_action' activesupport (7.0.2.3) lib/active_support/callbacks.rb:107:in run_callbacks'
actionpack (7.0.2.3) lib/abstract_controller/callbacks.rb:233:in process_action' actionpack (7.0.2.3) lib/action_controller/metal/rescue.rb:22:in process_action'
actionpack (7.0.2.3) lib/action_controller/metal/instrumentation.rb:67:in block in process_action' activesupport (7.0.2.3) lib/active_support/notifications.rb:206:in block in instrument'
activesupport (7.0.2.3) lib/active_support/notifications/instrumenter.rb:24:in instrument' activesupport (7.0.2.3) lib/active_support/notifications.rb:206:in instrument'
actionpack (7.0.2.3) lib/action_controller/metal/instrumentation.rb:66:in process_action' actionpack (7.0.2.3) lib/action_controller/metal/params_wrapper.rb:259:in process_action'
activerecord (7.0.2.3) lib/active_record/railties/controller_runtime.rb:27:in process_action' actionpack (7.0.2.3) lib/abstract_controller/base.rb:151:in process'
actionpack (7.0.2.3) lib/action_controller/metal.rb:188:in dispatch' actionpack (7.0.2.3) lib/action_controller/metal.rb:251:in dispatch'
actionpack (7.0.2.3) lib/action_dispatch/routing/route_set.rb:49:in dispatch' actionpack (7.0.2.3) lib/action_dispatch/routing/route_set.rb:32:in serve'
actionpack (7.0.2.3) lib/action_dispatch/journey/router.rb:50:in block in serve' actionpack (7.0.2.3) lib/action_dispatch/journey/router.rb:32:in each'
actionpack (7.0.2.3) lib/action_dispatch/journey/router.rb:32:in serve' actionpack (7.0.2.3) lib/action_dispatch/routing/route_set.rb:850:in call'
rack (2.2.3) lib/rack/etag.rb:27:in call' rack (2.2.3) lib/rack/conditional_get.rb:40:in call'
rack (2.2.3) lib/rack/head.rb:12:in call' activerecord (7.0.2.3) lib/active_record/migration.rb:603:in call'
actionpack (7.0.2.3) lib/action_dispatch/middleware/callbacks.rb:27:in block in call' activesupport (7.0.2.3) lib/active_support/callbacks.rb:99:in run_callbacks'
actionpack (7.0.2.3) lib/action_dispatch/middleware/callbacks.rb:26:in call' actionpack (7.0.2.3) lib/action_dispatch/middleware/executor.rb:14:in call'
actionpack (7.0.2.3) lib/action_dispatch/middleware/actionable_exceptions.rb:17:in call' actionpack (7.0.2.3) lib/action_dispatch/middleware/debug_exceptions.rb:28:in call'
actionpack (7.0.2.3) lib/action_dispatch/middleware/show_exceptions.rb:26:in call' railties (7.0.2.3) lib/rails/rack/logger.rb:36:in call_app'
railties (7.0.2.3) lib/rails/rack/logger.rb:25:in block in call' activesupport (7.0.2.3) lib/active_support/tagged_logging.rb:99:in block in tagged'
activesupport (7.0.2.3) lib/active_support/tagged_logging.rb:37:in tagged' activesupport (7.0.2.3) lib/active_support/tagged_logging.rb:99:in tagged'
railties (7.0.2.3) lib/rails/rack/logger.rb:25:in call' actionpack (7.0.2.3) lib/action_dispatch/middleware/remote_ip.rb:93:in call'
request_store (1.5.1) lib/request_store/middleware.rb:19:in call' actionpack (7.0.2.3) lib/action_dispatch/middleware/request_id.rb:26:in call'
rack (2.2.3) lib/rack/runtime.rb:22:in call' activesupport (7.0.2.3) lib/active_support/cache/strategy/local_cache_middleware.rb:29:in call'
actionpack (7.0.2.3) lib/action_dispatch/middleware/server_timing.rb:20:in call' actionpack (7.0.2.3) lib/action_dispatch/middleware/executor.rb:14:in call'
actionpack (7.0.2.3) lib/action_dispatch/middleware/static.rb:23:in call' rack (2.2.3) lib/rack/sendfile.rb:110:in call'
actionpack (7.0.2.3) lib/action_dispatch/middleware/host_authorization.rb:137:in call' rack-cors (1.1.1) lib/rack/cors.rb:100:in call'
railties (7.0.2.3) lib/rails/engine.rb:530:in call' puma (5.6.2) lib/puma/configuration.rb:252:in call'
puma (5.6.2) lib/puma/request.rb:77:in block in handle_request' puma (5.6.2) lib/puma/thread_pool.rb:340:in with_force_shutdown'
puma (5.6.2) lib/puma/request.rb:76:in handle_request' puma (5.6.2) lib/puma/server.rb:441:in process_client'
puma (5.6.2) lib/puma/thread_pool.rb:147:in block in spawn_thread'

Originally posted by @JulesDebeaumont in #97 (comment)

Disabling registration

Hi again, and thank you for this gem! I'd like to disable the registration functionality, so an user can't just sign up for my service -- he must be invited to get access.

How would I do this? I don't see a Registerable module I can just not include in my user model.

Thanks!

Helper method 'authenticate' does not work

Hey, it seems that 'authenticate' method, without bang, does not work:

module V2
  class TemplatesController < ApplicationController
    before_action :authenticate

    def index
      render json: Template.all.to_json, status: :ok
    end
  end
end


RSpec.describe V2::TemplatesController, type: :controller do
  describe '#index' do
    context 'when unauthorized' do
      it 'responds with error' do
        get :index

        expect(response).to have_http_status 401
      end
    end
  end
end

RailsJwtAuth::AuthenticableHelper included in ApplicationController
RailsJwtAuth::Authenticatable included in User model
RailsJwtAuth::SpecHelpers included in rspec_helper.rb

I expect to have 401 error response but I get 200 and json in response body. Did I forget something or that's a bug?

Besides that, authenticate! responds with odd errors:

  1. No token provided:
JWT::DecodeError at /v2/templates
=================================

> Nil JSON web token

jwt (2.2.1) lib/jwt/decode.rb, line 12
--------------------------------------

    7   # JWT::Decode module
    8   module JWT
    9     # Decoding logic for JWT
   10     class Decode
   11       def initialize(jwt, key, verify, options, &keyfinder)
>  12         raise(JWT::DecodeError, 'Nil JSON web token') unless jwt
   13         @jwt = jwt
   14         @key = key
   15         @options = options
   16         @segments = jwt.split('.')
   17         @verify = verify

App backtrace
-------------

Full backtrace
--------------

 - jwt (2.2.1) lib/jwt/decode.rb:12:in `initialize'
 - jwt (2.2.1) lib/jwt.rb:28:in `decode'
 - rails_jwt_auth (1.5.0) lib/rails_jwt_auth/jwt_manager.rb:18:in `decode'
 - rails_jwt_auth (1.5.0) lib/rails_jwt_auth/jwt_manager.rb:30:in `decode_from_request'
 - rails_jwt_auth (1.5.0) app/controllers/concerns/rails_jwt_auth/authenticable_helper.rb:19:in `authenticate!'
  1. Token has expired:
JWT::ExpiredSignature at /v2/templates
======================================

> Signature has expired

jwt (2.2.1) lib/jwt/verify.rb, line 41
--------------------------------------

   36         raise(JWT::InvalidAudError, "Invalid audience. Expected #{options_aud}, received #{aud || '<none>'}") if ([*aud] & [*options_aud]).empty?
   37       end
   38   
   39       def verify_expiration
   40         return unless @payload.include?('exp')
>  41         raise(JWT::ExpiredSignature, 'Signature has expired') if @payload['exp'].to_i <= (Time.now.to_i - exp_leeway)
   42       end
   43   
   44       def verify_iat
   45         return unless @payload.include?('iat')
   46   

App backtrace
-------------

Full backtrace
--------------

 - jwt (2.2.1) lib/jwt/verify.rb:41:in `verify_expiration'
 - jwt (2.2.1) lib/jwt/verify.rb:15:in `block (2 levels) in singleton class'
 - jwt (2.2.1) lib/jwt/verify.rb:22:in `block in verify_claims'
 - jwt (2.2.1) lib/jwt/verify.rb:20:in `verify_claims'
 - jwt (2.2.1) lib/jwt/decode.rb:64:in `verify_claims'
 - jwt (2.2.1) lib/jwt/decode.rb:27:in `decode_segments'
 - jwt (2.2.1) lib/jwt.rb:28:in `decode'
 - rails_jwt_auth (1.5.0) lib/rails_jwt_auth/jwt_manager.rb:18:in `decode'
 - rails_jwt_auth (1.5.0) lib/rails_jwt_auth/jwt_manager.rb:30:in `decode_from_request'
 - rails_jwt_auth (1.5.0) app/controllers/concerns/rails_jwt_auth/authenticable_helper.rb:19:in `authenticate!'
  1. Invalid token:
JWT::DecodeError at /v2/templates
=================================

> Not enough or too many segments

jwt (2.2.1) lib/jwt/decode.rb, line 71
--------------------------------------

   66   
   67       def validate_segment_count!
   68         return if segment_length == 3
   69         return if !@verify && segment_length == 2 # If no verifying required, the signature is not needed
   70   
>  71         raise(JWT::DecodeError, 'Not enough or too many segments')
   72       end
   73   
   74       def segment_length
   75         @segments.count
   76       end

App backtrace
-------------

Full backtrace
--------------

 - jwt (2.2.1) lib/jwt/decode.rb:71:in `validate_segment_count!'
 - jwt (2.2.1) lib/jwt/decode.rb:23:in `decode_segments'
 - jwt (2.2.1) lib/jwt.rb:28:in `decode'
 - rails_jwt_auth (1.5.0) lib/rails_jwt_auth/jwt_manager.rb:18:in `decode'
 - rails_jwt_auth (1.5.0) lib/rails_jwt_auth/jwt_manager.rb:30:in `decode_from_request'
 - rails_jwt_auth (1.5.0) app/controllers/concerns/rails_jwt_auth/authenticable_helper.rb:19:in `authenticate!'

Receiving 'invalid' response when trying to update password

I'm trying to update my user's password from my front-end, and I am receiving an 'invalid' error message, which is coming from here:
https://github.com/rjurado01/rails_jwt_auth/blob/master/app/models/concerns/rails_jwt_auth/authenticatable.rb#L46

I am using a custom controller, based on the docs. This is what is returning the 'invalid' message:

      current_user.update_with_password(
        update_params.merge(auth_tokens: [jwt_payload['auth_token']])
      )

It is the correct password, so I'm not sure what is happening. It is trying to call an authenticate(current_password) method, but I can't even find that definition in the codebase.

Am I missing something?

Error when executing: rails g rails_jwt_auth:install

Hi! I just wanted to try this gem with fresh Rails 6 app but I encountered an error while executing the install generator command.

ruby 3.0.1p64
Rails 6.1.3.2

I generated my new Rails app with these options (options part is probably not importat):

rails new 'name-of-the-app' --api --skip-action-mailbox --skip-action-text --skip-active-storage -T

After this I added 'rails_jwt_auth' gem to a Gemfile:

gem 'rails_jwt_auth', '~> 2.0', '>= 2.0.2'

but I encountered an error when executing the install command from the documentation:

rails g rails_jwt_auth:install

This is the error I was getting:

/home/my-user-name/.rvm/gems/ruby-3.0.1/gems/rails_jwt_auth-2.0.2/lib/rails_jwt_auth.rb:30:in <module:RailsJwtAuth>': undefined method days' for 7:Integer (NoMethodError)

I found this: rails/rails#40794

And I was able to fix the issue by requiring 'active_support/core_ext/integer/time' inside a gem

I uploaded a screenshot so you can see better what I am talking about.
fix

After adding this line you can see on the screenshot I was able to run:

rails g rails_jwt_auth:install

Now, maybe this is not the best way to approach this issue but I was able to execute the install command after requiring this module.

Regards,
Dusan

Generate secure ramdom password

special_char = "!\"\#$%&'()*+,-./".chars.sample
"#{SecureRandom.base58(14)}#{rand(9)}#{special_char}"

or

SecureRandom.urlsafe_base64(16, true)

Downcase del email y mejorar filtro

El filtro que valida el email permite mayúsculas y acentos. Hacer el filtro más estricto para impedir acentos y añadir un hook para hacer downcase del email.

Run before_action on all actions except sign in/sign up

Thank you for this gem! My application_controller.rb currently looks like this:

class ApplicationController < ActionController::API
    include RailsJwtAuth::AuthenticableHelper

    before_action :authenticate!
end

I'd like to ensure every action is authenticated, so I'd prefer to put before_action :authenticate! into ApplicationController instead of into every controller separately.

However, if I do this, then when I make a POST request to /session (in order to acquire a JWT token), I get a RailsJwtAuth::NotAuthorized exception.

How can I whitelist some of the actions from rails_jwt_auth to prevent this?

Sending wrong confirmation code

Hello,

I am experiencing an issue where the gem is sending the incorrect confirmation_token.

If I try to update a users email, I get sent a confirmation link, however, that confirmation link is incorrect, and the confirmation_token is referring to the LAST confirmation_token on the User, not the newly generated one, and therefore confirmations do not work.

I believe the fix to this would be to add:
return false unless save

to here:
https://github.com/rjurado01/rails_jwt_auth/blob/master/app/models/concerns/rails_jwt_auth/confirmable.rb#L35

Like you did here:
https://github.com/rjurado01/rails_jwt_auth/blob/master/app/models/concerns/rails_jwt_auth/confirmable.rb#L59

No access to RailsJwtAuth::AuthenticableHelper methods in actioncable channels

Hi, I think there's a problem here. I do include the RailsJwtAuth::AuthenticableHelper in actioncable channel and it doesn't recognize the methods and returns nil.
channel codes:

class ExampleChannel < ApplicationCable::Channel
	include RailsJwtAuth::AuthenticableHelper
	def subscribed
        pp current_user # => nil
....

Am I doing it wrong ?

Hacer que la gema distinga cuando se manda un email inválido de un email vacío

def create
      return render_422(email: [{error: :blank}]) if password_create_params[:email].blank?

      user = RailsJwtAuth.model.where(email: password_create_params[:email].to_s.downcase).first
      return render_422(email: [{error: :not_found}]) unless user

      user.send_reset_password_instructions ? render_204 : render_422(user.errors.details)
end

Claves en el locale están mal

Error:

en:
  rails_jwt_auth:
    mailer:
      confirmation_instructions:
        subject: "Confirmation instructions"
      reset_password_instructions.subject: <------------------------------
        subject: "Reset password instructions"
    errors:
      unconfirmed: "unconfirmed email"
      already_confirmed: "was already confirmed, please try signing in"
      create_session: "invalid %{field} / password"
      expired: "has expired, please request a new one"
      invalid: "invalid"
      blank: "blank"
      not_found: "not found"
      email:
        invlid: "is not an email" <------------------------------
      current_password:
        blank: "blank"
        invalid: "invalid"
      password:
blank: "blank"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.