otp-jwt 0.2.4 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
 - data/LICENSE.txt +21 -0
 - data/README.md +8 -0
 - data/lib/otp/active_record.rb +0 -1
 - data/lib/otp/jwt/action_controller.rb +0 -1
 - data/lib/otp/jwt/active_record.rb +7 -3
 - data/lib/otp/jwt/test_helpers.rb +1 -1
 - data/lib/otp/jwt/version.rb +1 -1
 - data/spec/dummy.rb +82 -0
 - data/spec/otp/jwt/token_spec.rb +54 -0
 - data/spec/spec_helper.rb +70 -0
 - data/spec/tokens_controller_spec.rb +50 -0
 - data/spec/user_spec.rb +63 -0
 - data/spec/users_controller_spec.rb +23 -0
 - metadata +47 -21
 - data/.github/main.workflow +0 -44
 - data/.gitignore +0 -4
 - data/.rspec +0 -3
 - data/.rubocop.yml +0 -46
 - data/.yardstick.yml +0 -29
 - data/Gemfile +0 -7
 - data/Rakefile +0 -30
 - data/otp-jwt.gemspec +0 -36
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 2 
     | 
    
         
            +
            SHA256:
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: e7d17e5145dd260643ff8c5af4e8bdd866282b126d9873d0eb7db432fa289e97
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: bdedfcdb0ad970bc380f18aa4e518030b15b08f439d0854043ac45d22ba55c81
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: b287bc6b7d738be7ef9028d224a77794a27e1da869115de54b693ee9b6c7f5ce094672bf021e4b79956171bae351fd36c39b2506ce4d0c58f5081a43679294d5
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: bdae8f51b91ef4e488d867b9281a604437524dcf0c40486901402393341ca563ba465c3cc2eb554f76ebda928124a2ae382302acd418852311368c98f39c08a5
         
     | 
    
        data/LICENSE.txt
    ADDED
    
    | 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            The MIT License (MIT)
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            Copyright (c) 2021 Stas Suscov
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            Permission is hereby granted, free of charge, to any person obtaining a copy
         
     | 
| 
      
 6 
     | 
    
         
            +
            of this software and associated documentation files (the "Software"), to deal
         
     | 
| 
      
 7 
     | 
    
         
            +
            in the Software without restriction, including without limitation the rights
         
     | 
| 
      
 8 
     | 
    
         
            +
            to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         
     | 
| 
      
 9 
     | 
    
         
            +
            copies of the Software, and to permit persons to whom the Software is
         
     | 
| 
      
 10 
     | 
    
         
            +
            furnished to do so, subject to the following conditions:
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            The above copyright notice and this permission notice shall be included in
         
     | 
| 
      
 13 
     | 
    
         
            +
            all copies or substantial portions of the Software.
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         
     | 
| 
      
 16 
     | 
    
         
            +
            IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         
     | 
| 
      
 17 
     | 
    
         
            +
            FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         
     | 
| 
      
 18 
     | 
    
         
            +
            AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         
     | 
| 
      
 19 
     | 
    
         
            +
            LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         
     | 
| 
      
 20 
     | 
    
         
            +
            OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
         
     | 
| 
      
 21 
     | 
    
         
            +
            THE SOFTWARE.
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -10,6 +10,8 @@ One time password (email, SMS) authentication support for HTTP APIs. 
     | 
|
| 
       10 
10 
     | 
    
         
             
            This project provides a couple of mixins to help you build
         
     | 
| 
       11 
11 
     | 
    
         
             
            applications/HTTP APIs without asking your users to provide passwords.
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
      
 13 
     | 
    
         
            +
            [Your browser probably can work seamlessly with OTPs](https://web.dev/web-otp/)!!! :heart_eyes: 
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
       13 
15 
     | 
    
         
             
            ## About
         
     | 
| 
       14 
16 
     | 
    
         | 
| 
       15 
17 
     | 
    
         
             
            The goal of this project is to provide support for one time passwords
         
     | 
| 
         @@ -103,6 +105,12 @@ A migration to add these two looks like this: 
     | 
|
| 
       103 
105 
     | 
    
         
             
            $ rails g migration add_otp_to_users otp_secret:string otp_counter:integer
         
     | 
| 
       104 
106 
     | 
    
         
             
            ```
         
     | 
| 
       105 
107 
     | 
    
         | 
| 
      
 108 
     | 
    
         
            +
            Generate `opt_secret` by running the following in rails console if you have preexisting user data:
         
     | 
| 
      
 109 
     | 
    
         
            +
            ```
         
     | 
| 
      
 110 
     | 
    
         
            +
            User.all.each do |u|
         
     | 
| 
      
 111 
     | 
    
         
            +
              u.save()
         
     | 
| 
      
 112 
     | 
    
         
            +
            end
         
     | 
| 
      
 113 
     | 
    
         
            +
            ```
         
     | 
| 
       106 
114 
     | 
    
         
             
            #### Mailer support
         
     | 
| 
       107 
115 
     | 
    
         | 
| 
       108 
116 
     | 
    
         
             
            You can use the built-in mailer to deliver the OTP, just require it and
         
     | 
    
        data/lib/otp/active_record.rb
    CHANGED
    
    
| 
         @@ -17,9 +17,13 @@ module OTP 
     | 
|
| 
       17 
17 
     | 
    
         
             
                        val = payload[claim_name]
         
     | 
| 
       18 
18 
     | 
    
         
             
                        pk_col = self.column_for_attribute(self.primary_key)
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
                        # Arel casts the values to the primary key type, 
     | 
| 
       21 
     | 
    
         
            -
                        # that an UUID  
     | 
| 
       22 
     | 
    
         
            -
                         
     | 
| 
      
 20 
     | 
    
         
            +
                        # Arel casts the values to the primary key type,
         
     | 
| 
      
 21 
     | 
    
         
            +
                        # which means that an UUID becomes an integer by default...
         
     | 
| 
      
 22 
     | 
    
         
            +
                        if self.connection.respond_to?(:type_cast_from_column)
         
     | 
| 
      
 23 
     | 
    
         
            +
                          casted_val = self.connection.type_cast_from_column(pk_col, val)
         
     | 
| 
      
 24 
     | 
    
         
            +
                        else
         
     | 
| 
      
 25 
     | 
    
         
            +
                          casted_val = self.connection.type_cast(val, pk_col)
         
     | 
| 
      
 26 
     | 
    
         
            +
                        end
         
     | 
| 
       23 
27 
     | 
    
         | 
| 
       24 
28 
     | 
    
         
             
                        return if casted_val.to_s != val.to_s.strip
         
     | 
| 
       25 
29 
     | 
    
         | 
    
        data/lib/otp/jwt/test_helpers.rb
    CHANGED
    
    | 
         @@ -15,7 +15,7 @@ module OTP 
     | 
|
| 
       15 
15 
     | 
    
         
             
                  #
         
     | 
| 
       16 
16 
     | 
    
         
             
                  # @return [Hash] the authorization headers
         
     | 
| 
       17 
17 
     | 
    
         
             
                  def jwt_auth_header(entity_or_subject)
         
     | 
| 
       18 
     | 
    
         
            -
                    return json_headers  
     | 
| 
      
 18 
     | 
    
         
            +
                    return json_headers if entity_or_subject.blank?
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
20 
     | 
    
         
             
                    token = entity_or_subject.try(:to_jwt)
         
     | 
| 
       21 
21 
     | 
    
         
             
                    token ||= OTP::JWT::Token.sign(sub: entity_or_subject)
         
     | 
    
        data/lib/otp/jwt/version.rb
    CHANGED
    
    
    
        data/spec/dummy.rb
    ADDED
    
    | 
         @@ -0,0 +1,82 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'active_record/railtie'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'action_controller/railtie'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'global_id/railtie'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'otp/mailer'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            class Dummy < Rails::Application
         
     | 
| 
      
 7 
     | 
    
         
            +
              secrets.secret_key_base = '_'
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              config.hosts << 'www.example.com' if config.respond_to?(:hosts)
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              config.logger = Logger.new($stdout)
         
     | 
| 
      
 12 
     | 
    
         
            +
              config.logger.level = ENV['LOG_LEVEL'] || Logger::WARN
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              routes.draw do
         
     | 
| 
      
 15 
     | 
    
         
            +
                resources :users, only: [:index]
         
     | 
| 
      
 16 
     | 
    
         
            +
                resources :tokens, only: [:create]
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            GlobalID.app = Dummy
         
     | 
| 
      
 21 
     | 
    
         
            +
            Rails.logger = Dummy.config.logger
         
     | 
| 
      
 22 
     | 
    
         
            +
            ActiveRecord::Base.logger = Dummy.config.logger
         
     | 
| 
      
 23 
     | 
    
         
            +
            ActiveRecord::Base.establish_connection(
         
     | 
| 
      
 24 
     | 
    
         
            +
              ENV['DATABASE_URL'] || 'sqlite3::memory:'
         
     | 
| 
      
 25 
     | 
    
         
            +
            )
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            ActiveRecord::Schema.define do
         
     | 
| 
      
 28 
     | 
    
         
            +
              create_table :users, force: true do |t|
         
     | 
| 
      
 29 
     | 
    
         
            +
                t.string :email
         
     | 
| 
      
 30 
     | 
    
         
            +
                t.string :full_name
         
     | 
| 
      
 31 
     | 
    
         
            +
                t.string :phone_number
         
     | 
| 
      
 32 
     | 
    
         
            +
                t.string :otp_secret
         
     | 
| 
      
 33 
     | 
    
         
            +
                t.integer :otp_counter
         
     | 
| 
      
 34 
     | 
    
         
            +
                t.timestamp :last_login_at
         
     | 
| 
      
 35 
     | 
    
         
            +
                t.timestamps
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
            end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            class User < ActiveRecord::Base
         
     | 
| 
      
 40 
     | 
    
         
            +
              include GlobalID::Identification
         
     | 
| 
      
 41 
     | 
    
         
            +
              include OTP::ActiveRecord
         
     | 
| 
      
 42 
     | 
    
         
            +
              include OTP::JWT::ActiveRecord
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              def email_otp
         
     | 
| 
      
 45 
     | 
    
         
            +
                OTP::Mailer.otp(email, otp, self).deliver_later
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
            end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
            class ApplicationController < ActionController::Base
         
     | 
| 
      
 50 
     | 
    
         
            +
              include OTP::JWT::ActionController
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
              private
         
     | 
| 
      
 53 
     | 
    
         
            +
              def current_user
         
     | 
| 
      
 54 
     | 
    
         
            +
                @jwt_user ||= User.from_jwt(request_authorization_header)
         
     | 
| 
      
 55 
     | 
    
         
            +
              end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
              def current_user!
         
     | 
| 
      
 58 
     | 
    
         
            +
                current_user || raise('User authentication failed')
         
     | 
| 
      
 59 
     | 
    
         
            +
              rescue
         
     | 
| 
      
 60 
     | 
    
         
            +
                head(:unauthorized)
         
     | 
| 
      
 61 
     | 
    
         
            +
              end
         
     | 
| 
      
 62 
     | 
    
         
            +
            end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
            class UsersController < ApplicationController
         
     | 
| 
      
 65 
     | 
    
         
            +
              before_action :current_user!
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
              def index
         
     | 
| 
      
 68 
     | 
    
         
            +
                render json: current_user
         
     | 
| 
      
 69 
     | 
    
         
            +
              end
         
     | 
| 
      
 70 
     | 
    
         
            +
            end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
            class TokensController < ApplicationController
         
     | 
| 
      
 73 
     | 
    
         
            +
              def create
         
     | 
| 
      
 74 
     | 
    
         
            +
                user = User.find_by(email: params[:email])
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                jwt_from_otp(user, params[:otp]) do |auth_user|
         
     | 
| 
      
 77 
     | 
    
         
            +
                  auth_user.update_column(:last_login_at, DateTime.current)
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                  render json: { token: auth_user.to_jwt }, status: :created
         
     | 
| 
      
 80 
     | 
    
         
            +
                end
         
     | 
| 
      
 81 
     | 
    
         
            +
              end
         
     | 
| 
      
 82 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,54 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            RSpec.describe OTP::JWT::Token, type: :model do
         
     | 
| 
      
 4 
     | 
    
         
            +
              let(:payload) { { 'sub' => FFaker::Internet.password } }
         
     | 
| 
      
 5 
     | 
    
         
            +
              let(:token) do
         
     | 
| 
      
 6 
     | 
    
         
            +
                JWT.encode(
         
     | 
| 
      
 7 
     | 
    
         
            +
                  payload.dup.merge(exp: Time.now.to_i + described_class.jwt_lifetime),
         
     | 
| 
      
 8 
     | 
    
         
            +
                  described_class.jwt_signature_key,
         
     | 
| 
      
 9 
     | 
    
         
            +
                  described_class.jwt_algorithm
         
     | 
| 
      
 10 
     | 
    
         
            +
                )
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              describe '#sign' do
         
     | 
| 
      
 14 
     | 
    
         
            +
                it { expect(described_class.sign(payload)).to eq(token) }
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              describe '#verify' do
         
     | 
| 
      
 18 
     | 
    
         
            +
                it do
         
     | 
| 
      
 19 
     | 
    
         
            +
                  expect(described_class.verify(token).first).to include(payload)
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                it 'with a bad token' do
         
     | 
| 
      
 23 
     | 
    
         
            +
                  expect { described_class.verify(FFaker::Internet.password) }
         
     | 
| 
      
 24 
     | 
    
         
            +
                    .to raise_error(JWT::DecodeError)
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                it 'with an expired token' do
         
     | 
| 
      
 28 
     | 
    
         
            +
                  token = OTP::JWT::Token.sign(
         
     | 
| 
      
 29 
     | 
    
         
            +
                    sub: FFaker::Internet.password, exp: DateTime.now.to_i
         
     | 
| 
      
 30 
     | 
    
         
            +
                  )
         
     | 
| 
      
 31 
     | 
    
         
            +
                  expect { described_class.verify(token) }
         
     | 
| 
      
 32 
     | 
    
         
            +
                    .to raise_error(JWT::ExpiredSignature)
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              describe '#decode' do
         
     | 
| 
      
 37 
     | 
    
         
            +
                let(:user) { create_user }
         
     | 
| 
      
 38 
     | 
    
         
            +
                let(:payload) { { 'sub' => user.id } }
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                it do
         
     | 
| 
      
 41 
     | 
    
         
            +
                  expect(
         
     | 
| 
      
 42 
     | 
    
         
            +
                    described_class.decode(token) { |p| User.find(p['sub']) }
         
     | 
| 
      
 43 
     | 
    
         
            +
                  ).to eq(user)
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                context 'with a bad token' do
         
     | 
| 
      
 47 
     | 
    
         
            +
                  let(:token) { FFaker::Internet.password }
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  it do
         
     | 
| 
      
 50 
     | 
    
         
            +
                    expect(described_class.decode(token)).to eq(nil)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
              end
         
     | 
| 
      
 54 
     | 
    
         
            +
            end
         
     | 
    
        data/spec/spec_helper.rb
    ADDED
    
    | 
         @@ -0,0 +1,70 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'bundler/setup'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'simplecov'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            SimpleCov.start do
         
     | 
| 
      
 5 
     | 
    
         
            +
              add_group 'Lib', 'lib'
         
     | 
| 
      
 6 
     | 
    
         
            +
              add_group 'Tests', 'spec'
         
     | 
| 
      
 7 
     | 
    
         
            +
            end
         
     | 
| 
      
 8 
     | 
    
         
            +
            SimpleCov.minimum_coverage 90
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            require 'otp'
         
     | 
| 
      
 11 
     | 
    
         
            +
            require 'otp/jwt'
         
     | 
| 
      
 12 
     | 
    
         
            +
            require 'otp/jwt/test_helpers'
         
     | 
| 
      
 13 
     | 
    
         
            +
            require_relative 'dummy'
         
     | 
| 
      
 14 
     | 
    
         
            +
            require 'ffaker'
         
     | 
| 
      
 15 
     | 
    
         
            +
            require 'rspec/rails'
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            OTP::JWT::Token.jwt_signature_key = '_'
         
     | 
| 
      
 18 
     | 
    
         
            +
            OTP::Mailer.default from: '_'
         
     | 
| 
      
 19 
     | 
    
         
            +
            ActiveJob::Base.queue_adapter = :test
         
     | 
| 
      
 20 
     | 
    
         
            +
            ActionMailer::Base.delivery_method = :test
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            module OTP::JWT::FactoryHelpers
         
     | 
| 
      
 23 
     | 
    
         
            +
              # Creates an user
         
     | 
| 
      
 24 
     | 
    
         
            +
              #
         
     | 
| 
      
 25 
     | 
    
         
            +
              # @return [User]
         
     | 
| 
      
 26 
     | 
    
         
            +
              def create_user
         
     | 
| 
      
 27 
     | 
    
         
            +
                User.create!(
         
     | 
| 
      
 28 
     | 
    
         
            +
                  full_name: FFaker::Name.name,
         
     | 
| 
      
 29 
     | 
    
         
            +
                  email: FFaker::Internet.email,
         
     | 
| 
      
 30 
     | 
    
         
            +
                  phone_number: FFaker::PhoneNumber.phone_number
         
     | 
| 
      
 31 
     | 
    
         
            +
                )
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
            end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            module Rails4RequestMethods
         
     | 
| 
      
 36 
     | 
    
         
            +
              [:get, :post, :put, :delete].each do |method_name|
         
     | 
| 
      
 37 
     | 
    
         
            +
                define_method(method_name) do |path, named_args|
         
     | 
| 
      
 38 
     | 
    
         
            +
                  super(
         
     | 
| 
      
 39 
     | 
    
         
            +
                    path,
         
     | 
| 
      
 40 
     | 
    
         
            +
                    named_args.delete(:params),
         
     | 
| 
      
 41 
     | 
    
         
            +
                    named_args.delete(:headers)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  )
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
            end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
            RSpec.configure do |config|
         
     | 
| 
      
 48 
     | 
    
         
            +
              config.use_transactional_fixtures = true
         
     | 
| 
      
 49 
     | 
    
         
            +
              config.mock_with :rspec
         
     | 
| 
      
 50 
     | 
    
         
            +
              config.filter_run_when_matching :focus
         
     | 
| 
      
 51 
     | 
    
         
            +
              config.disable_monkey_patching!
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
              config.expect_with :rspec do |c|
         
     | 
| 
      
 54 
     | 
    
         
            +
                c.syntax = :expect
         
     | 
| 
      
 55 
     | 
    
         
            +
              end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
              config.include OTP::JWT::TestHelpers, type: :model
         
     | 
| 
      
 58 
     | 
    
         
            +
              config.include OTP::JWT::FactoryHelpers, type: :model
         
     | 
| 
      
 59 
     | 
    
         
            +
              config.include ActiveJob::TestHelper, type: :model
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
              config.include OTP::JWT::TestHelpers, type: :request
         
     | 
| 
      
 62 
     | 
    
         
            +
              config.include OTP::JWT::FactoryHelpers, type: :request
         
     | 
| 
      
 63 
     | 
    
         
            +
              config.include ActiveJob::TestHelper, type: :request
         
     | 
| 
      
 64 
     | 
    
         
            +
              config.include Dummy.routes.url_helpers, type: :request
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
              if ::Rails::VERSION::MAJOR == 4
         
     | 
| 
      
 67 
     | 
    
         
            +
                config.include Rails4RequestMethods, type: :request
         
     | 
| 
      
 68 
     | 
    
         
            +
                config.include Rails4RequestMethods, type: :controller
         
     | 
| 
      
 69 
     | 
    
         
            +
              end
         
     | 
| 
      
 70 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,50 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            RSpec.describe TokensController, type: :request do
         
     | 
| 
      
 4 
     | 
    
         
            +
              let(:user) { create_user }
         
     | 
| 
      
 5 
     | 
    
         
            +
              let(:params) { }
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              around do |examp|
         
     | 
| 
      
 8 
     | 
    
         
            +
                perform_enqueued_jobs(&examp)
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              before do
         
     | 
| 
      
 12 
     | 
    
         
            +
                ActionMailer::Base.deliveries.clear
         
     | 
| 
      
 13 
     | 
    
         
            +
                ActiveJob::Base.queue_adapter.performed_jobs.clear
         
     | 
| 
      
 14 
     | 
    
         
            +
                post(tokens_path, params: params.to_json, headers: json_headers)
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              it { expect(response).to have_http_status(:forbidden) }
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              context 'with good email and no otp' do
         
     | 
| 
      
 20 
     | 
    
         
            +
                let(:params) { { email: user.email } }
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                it do
         
     | 
| 
      
 23 
     | 
    
         
            +
                  expect(response).to have_http_status(:bad_request)
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  mail = ActionMailer::Base.deliveries.last
         
     | 
| 
      
 26 
     | 
    
         
            +
                  expect(mail.subject).to eq(OTP::Mailer.default[:subject])
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              context 'with good email and bad otp' do
         
     | 
| 
      
 31 
     | 
    
         
            +
                let(:params) { { email: user.email, otp: FFaker::Internet.password } }
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                it do
         
     | 
| 
      
 34 
     | 
    
         
            +
                  expect(response).to have_http_status(:forbidden)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  expect(ActionMailer::Base.deliveries.size).to eq(0)
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
              context 'with good email and good otp' do
         
     | 
| 
      
 40 
     | 
    
         
            +
                let(:params) { { email: user.email, otp: user.otp } }
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                it do
         
     | 
| 
      
 43 
     | 
    
         
            +
                  expect(response).to have_http_status(:created)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  expect(User.from_jwt(response_json['token'])).to eq(user)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  expect(ActionMailer::Base.deliveries.size).to eq(0)
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                  expect(user.reload.last_login_at).not_to be_blank
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
            end
         
     | 
    
        data/spec/user_spec.rb
    ADDED
    
    | 
         @@ -0,0 +1,63 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            RSpec.describe User, type: :model do
         
     | 
| 
      
 4 
     | 
    
         
            +
              let(:user) { create_user }
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              it { expect(User.new.otp_secret).not_to be_blank }
         
     | 
| 
      
 7 
     | 
    
         
            +
              it { expect(User.new.deliver_otp).to be_blank }
         
     | 
| 
      
 8 
     | 
    
         
            +
              it { expect(User.new.otp).to be_blank }
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              describe '#from_jwt' do
         
     | 
| 
      
 11 
     | 
    
         
            +
                let(:token) { user.to_jwt }
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                it do
         
     | 
| 
      
 14 
     | 
    
         
            +
                  expect(User.from_jwt(token)).to eq(user)
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                context 'with a cast-able subject value' do
         
     | 
| 
      
 18 
     | 
    
         
            +
                  let(:token) { OTP::JWT::Token.sign(sub: user.id.to_s + '_text') }
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  it do
         
     | 
| 
      
 21 
     | 
    
         
            +
                    expect(User.from_jwt(token)).to be_nil
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                context 'with a custom claim name' do
         
     | 
| 
      
 26 
     | 
    
         
            +
                  let(:claim_value) { FFaker::Internet.password }
         
     | 
| 
      
 27 
     | 
    
         
            +
                  let(:token) { user.to_jwt(my_claim_name: claim_value) }
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  it do
         
     | 
| 
      
 30 
     | 
    
         
            +
                    expect(OTP::JWT::Token.decode(token)['my_claim_name'])
         
     | 
| 
      
 31 
     | 
    
         
            +
                      .to eq(claim_value)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    expect(User.from_jwt(token, 'my_claim_name')).to be_nil
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
              describe '#otp' do
         
     | 
| 
      
 38 
     | 
    
         
            +
                it do
         
     | 
| 
      
 39 
     | 
    
         
            +
                  expect { user.otp }.to change(user, :otp_counter).by(1)
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                context 'without a secret' do
         
     | 
| 
      
 43 
     | 
    
         
            +
                  it do
         
     | 
| 
      
 44 
     | 
    
         
            +
                    user.update_column(:otp_secret, nil)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    expect(user.otp).to be_nil
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
              end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
              describe '#verify_otp' do
         
     | 
| 
      
 51 
     | 
    
         
            +
                it 'increments the otp counter after verification' do
         
     | 
| 
      
 52 
     | 
    
         
            +
                  expect(user.verify_otp(user.otp)).to be_truthy
         
     | 
| 
      
 53 
     | 
    
         
            +
                  expect { user.verify_otp(user.otp) }.to change(user, :otp_counter).by(2)
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                context 'without a secret' do
         
     | 
| 
      
 57 
     | 
    
         
            +
                  it do
         
     | 
| 
      
 58 
     | 
    
         
            +
                    user.update_column(:otp_secret, nil)
         
     | 
| 
      
 59 
     | 
    
         
            +
                    expect(user.verify_otp(rand(1000..2000).to_s)).to be_nil
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
              end
         
     | 
| 
      
 63 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            RSpec.describe UsersController, type: :request do
         
     | 
| 
      
 4 
     | 
    
         
            +
              let(:user) { nil }
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              before do
         
     | 
| 
      
 7 
     | 
    
         
            +
                get(users_path, headers: jwt_auth_header(user))
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              it { expect(response).to have_http_status(:unauthorized) }
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              context 'with known subject token' do
         
     | 
| 
      
 13 
     | 
    
         
            +
                let(:user) { create_user }
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                it { expect(response).to have_http_status(:ok) }
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              context 'with bad subject' do
         
     | 
| 
      
 19 
     | 
    
         
            +
                let(:user) { FFaker::Internet.password }
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                it { expect(response).to have_http_status(:unauthorized) }
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: otp-jwt
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0.2. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.2.5
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Stas Suscov
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date:  
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2021-01-09 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: activesupport
         
     | 
| 
         @@ -30,28 +30,28 @@ dependencies: 
     | 
|
| 
       30 
30 
     | 
    
         
             
                requirements:
         
     | 
| 
       31 
31 
     | 
    
         
             
                - - "~>"
         
     | 
| 
       32 
32 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       33 
     | 
    
         
            -
                    version: 2 
     | 
| 
      
 33 
     | 
    
         
            +
                    version: '2'
         
     | 
| 
       34 
34 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       35 
35 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       36 
36 
     | 
    
         
             
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
       37 
37 
     | 
    
         
             
                requirements:
         
     | 
| 
       38 
38 
     | 
    
         
             
                - - "~>"
         
     | 
| 
       39 
39 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       40 
     | 
    
         
            -
                    version: 2 
     | 
| 
      
 40 
     | 
    
         
            +
                    version: '2'
         
     | 
| 
       41 
41 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       42 
42 
     | 
    
         
             
              name: rotp
         
     | 
| 
       43 
43 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
       44 
44 
     | 
    
         
             
                requirements:
         
     | 
| 
       45 
45 
     | 
    
         
             
                - - "~>"
         
     | 
| 
       46 
46 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       47 
     | 
    
         
            -
                    version: ' 
     | 
| 
      
 47 
     | 
    
         
            +
                    version: '6'
         
     | 
| 
       48 
48 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       49 
49 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       50 
50 
     | 
    
         
             
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
       51 
51 
     | 
    
         
             
                requirements:
         
     | 
| 
       52 
52 
     | 
    
         
             
                - - "~>"
         
     | 
| 
       53 
53 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       54 
     | 
    
         
            -
                    version: ' 
     | 
| 
      
 54 
     | 
    
         
            +
                    version: '6'
         
     | 
| 
       55 
55 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       56 
56 
     | 
    
         
             
              name: bundler
         
     | 
| 
       57 
57 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
         @@ -84,16 +84,16 @@ dependencies: 
     | 
|
| 
       84 
84 
     | 
    
         
             
              name: rails
         
     | 
| 
       85 
85 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
       86 
86 
     | 
    
         
             
                requirements:
         
     | 
| 
       87 
     | 
    
         
            -
                - - " 
     | 
| 
      
 87 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
       88 
88 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       89 
     | 
    
         
            -
                    version: ' 
     | 
| 
      
 89 
     | 
    
         
            +
                    version: '4'
         
     | 
| 
       90 
90 
     | 
    
         
             
              type: :development
         
     | 
| 
       91 
91 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       92 
92 
     | 
    
         
             
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
       93 
93 
     | 
    
         
             
                requirements:
         
     | 
| 
       94 
     | 
    
         
            -
                - - " 
     | 
| 
      
 94 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
       95 
95 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       96 
     | 
    
         
            -
                    version: ' 
     | 
| 
      
 96 
     | 
    
         
            +
                    version: '4'
         
     | 
| 
       97 
97 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       98 
98 
     | 
    
         
             
              name: rspec-rails
         
     | 
| 
       99 
99 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
         @@ -108,6 +108,20 @@ dependencies: 
     | 
|
| 
       108 
108 
     | 
    
         
             
                - - ">="
         
     | 
| 
       109 
109 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       110 
110 
     | 
    
         
             
                    version: '0'
         
     | 
| 
      
 111 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 112 
     | 
    
         
            +
              name: rubocop
         
     | 
| 
      
 113 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 114 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 115 
     | 
    
         
            +
                - - '='
         
     | 
| 
      
 116 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 117 
     | 
    
         
            +
                    version: '0.81'
         
     | 
| 
      
 118 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 119 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 120 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 121 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 122 
     | 
    
         
            +
                - - '='
         
     | 
| 
      
 123 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 124 
     | 
    
         
            +
                    version: '0.81'
         
     | 
| 
       111 
125 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       112 
126 
     | 
    
         
             
              name: rubocop-performance
         
     | 
| 
       113 
127 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
         @@ -166,6 +180,20 @@ dependencies: 
     | 
|
| 
       166 
180 
     | 
    
         
             
                    version: '0'
         
     | 
| 
       167 
181 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       168 
182 
     | 
    
         
             
              name: sqlite3
         
     | 
| 
      
 183 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 184 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 185 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 186 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 187 
     | 
    
         
            +
                    version: 1.3.6
         
     | 
| 
      
 188 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 189 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 190 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 191 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 192 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 193 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 194 
     | 
    
         
            +
                    version: 1.3.6
         
     | 
| 
      
 195 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 196 
     | 
    
         
            +
              name: tzinfo-data
         
     | 
| 
       169 
197 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
       170 
198 
     | 
    
         
             
                requirements:
         
     | 
| 
       171 
199 
     | 
    
         
             
                - - ">="
         
     | 
| 
         @@ -199,14 +227,8 @@ executables: [] 
     | 
|
| 
       199 
227 
     | 
    
         
             
            extensions: []
         
     | 
| 
       200 
228 
     | 
    
         
             
            extra_rdoc_files: []
         
     | 
| 
       201 
229 
     | 
    
         
             
            files:
         
     | 
| 
       202 
     | 
    
         
            -
            -  
     | 
| 
       203 
     | 
    
         
            -
            - ".gitignore"
         
     | 
| 
       204 
     | 
    
         
            -
            - ".rspec"
         
     | 
| 
       205 
     | 
    
         
            -
            - ".rubocop.yml"
         
     | 
| 
       206 
     | 
    
         
            -
            - ".yardstick.yml"
         
     | 
| 
       207 
     | 
    
         
            -
            - Gemfile
         
     | 
| 
      
 230 
     | 
    
         
            +
            - LICENSE.txt
         
     | 
| 
       208 
231 
     | 
    
         
             
            - README.md
         
     | 
| 
       209 
     | 
    
         
            -
            - Rakefile
         
     | 
| 
       210 
232 
     | 
    
         
             
            - lib/otp.rb
         
     | 
| 
       211 
233 
     | 
    
         
             
            - lib/otp/active_record.rb
         
     | 
| 
       212 
234 
     | 
    
         
             
            - lib/otp/jwt.rb
         
     | 
| 
         @@ -218,10 +240,15 @@ files: 
     | 
|
| 
       218 
240 
     | 
    
         
             
            - lib/otp/mailer.rb
         
     | 
| 
       219 
241 
     | 
    
         
             
            - lib/otp/mailer/otp.text.erb
         
     | 
| 
       220 
242 
     | 
    
         
             
            - lib/otp/sms_job.rb
         
     | 
| 
       221 
     | 
    
         
            -
            -  
     | 
| 
      
 243 
     | 
    
         
            +
            - spec/dummy.rb
         
     | 
| 
      
 244 
     | 
    
         
            +
            - spec/otp/jwt/token_spec.rb
         
     | 
| 
      
 245 
     | 
    
         
            +
            - spec/spec_helper.rb
         
     | 
| 
      
 246 
     | 
    
         
            +
            - spec/tokens_controller_spec.rb
         
     | 
| 
      
 247 
     | 
    
         
            +
            - spec/user_spec.rb
         
     | 
| 
      
 248 
     | 
    
         
            +
            - spec/users_controller_spec.rb
         
     | 
| 
       222 
249 
     | 
    
         
             
            homepage: https://github.com/stas/otp-jwt
         
     | 
| 
       223 
250 
     | 
    
         
             
            licenses:
         
     | 
| 
       224 
     | 
    
         
            -
            -  
     | 
| 
      
 251 
     | 
    
         
            +
            - MIT
         
     | 
| 
       225 
252 
     | 
    
         
             
            metadata: {}
         
     | 
| 
       226 
253 
     | 
    
         
             
            post_install_message: 
         
     | 
| 
       227 
254 
     | 
    
         
             
            rdoc_options: []
         
     | 
| 
         @@ -238,8 +265,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement 
     | 
|
| 
       238 
265 
     | 
    
         
             
                - !ruby/object:Gem::Version
         
     | 
| 
       239 
266 
     | 
    
         
             
                  version: '0'
         
     | 
| 
       240 
267 
     | 
    
         
             
            requirements: []
         
     | 
| 
       241 
     | 
    
         
            -
             
     | 
| 
       242 
     | 
    
         
            -
            rubygems_version: 2.5.2.2
         
     | 
| 
      
 268 
     | 
    
         
            +
            rubygems_version: 3.0.3
         
     | 
| 
       243 
269 
     | 
    
         
             
            signing_key: 
         
     | 
| 
       244 
270 
     | 
    
         
             
            specification_version: 4
         
     | 
| 
       245 
271 
     | 
    
         
             
            summary: Passwordless HTTP APIs
         
     | 
    
        data/.github/main.workflow
    DELETED
    
    | 
         @@ -1,44 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            workflow "Tests" {
         
     | 
| 
       2 
     | 
    
         
            -
              on = "push"
         
     | 
| 
       3 
     | 
    
         
            -
              resolves = [
         
     | 
| 
       4 
     | 
    
         
            -
                "rspec-ruby2.6_rails4",
         
     | 
| 
       5 
     | 
    
         
            -
                "rspec-ruby2.6_rails5",
         
     | 
| 
       6 
     | 
    
         
            -
                "rspec-ruby2.6_rails6"
         
     | 
| 
       7 
     | 
    
         
            -
              ]
         
     | 
| 
       8 
     | 
    
         
            -
            }
         
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
            action "rspec-ruby2.6_rails4" {
         
     | 
| 
       11 
     | 
    
         
            -
              uses = "docker://ruby:2.6-alpine"
         
     | 
| 
       12 
     | 
    
         
            -
              env = {
         
     | 
| 
       13 
     | 
    
         
            -
                RAILS_VERSION = "~> 4"
         
     | 
| 
       14 
     | 
    
         
            -
                SQLITE3_VERSION = "~> 1.3.6"
         
     | 
| 
       15 
     | 
    
         
            -
              }
         
     | 
| 
       16 
     | 
    
         
            -
              args = [
         
     | 
| 
       17 
     | 
    
         
            -
                "sh", "-c",
         
     | 
| 
       18 
     | 
    
         
            -
                "apk add -U git build-base sqlite-dev && bundle install && rake"
         
     | 
| 
       19 
     | 
    
         
            -
              ]
         
     | 
| 
       20 
     | 
    
         
            -
            }
         
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
            action "rspec-ruby2.6_rails5" {
         
     | 
| 
       23 
     | 
    
         
            -
              uses = "docker://ruby:2.6-alpine"
         
     | 
| 
       24 
     | 
    
         
            -
              needs = ["rspec-ruby2.6_rails4"]
         
     | 
| 
       25 
     | 
    
         
            -
              env = {
         
     | 
| 
       26 
     | 
    
         
            -
                RAILS_VERSION = "~> 5"
         
     | 
| 
       27 
     | 
    
         
            -
              }
         
     | 
| 
       28 
     | 
    
         
            -
              args = [
         
     | 
| 
       29 
     | 
    
         
            -
                "sh", "-c",
         
     | 
| 
       30 
     | 
    
         
            -
                "apk add -U git build-base sqlite-dev && bundle install && rake"
         
     | 
| 
       31 
     | 
    
         
            -
              ]
         
     | 
| 
       32 
     | 
    
         
            -
            }
         
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
            action "rspec-ruby2.6_rails6" {
         
     | 
| 
       35 
     | 
    
         
            -
              uses = "docker://ruby:2.6-alpine"
         
     | 
| 
       36 
     | 
    
         
            -
              needs = ["rspec-ruby2.6_rails5"]
         
     | 
| 
       37 
     | 
    
         
            -
              env = {
         
     | 
| 
       38 
     | 
    
         
            -
                RAILS_VERSION = "~> 6.0.0.rc1"
         
     | 
| 
       39 
     | 
    
         
            -
              }
         
     | 
| 
       40 
     | 
    
         
            -
              args = [
         
     | 
| 
       41 
     | 
    
         
            -
                "sh", "-c",
         
     | 
| 
       42 
     | 
    
         
            -
                "apk add -U git build-base sqlite-dev && bundle install && rake"
         
     | 
| 
       43 
     | 
    
         
            -
              ]
         
     | 
| 
       44 
     | 
    
         
            -
            }
         
     | 
    
        data/.gitignore
    DELETED
    
    
    
        data/.rspec
    DELETED
    
    
    
        data/.rubocop.yml
    DELETED
    
    | 
         @@ -1,46 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            inherit_gem:
         
     | 
| 
       2 
     | 
    
         
            -
              rubocop-rails_config:
         
     | 
| 
       3 
     | 
    
         
            -
                - config/rails.yml
         
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
            require:
         
     | 
| 
       6 
     | 
    
         
            -
              - rubocop-performance
         
     | 
| 
       7 
     | 
    
         
            -
              - rubocop-rspec
         
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
            Rails:
         
     | 
| 
       10 
     | 
    
         
            -
              Enabled: true
         
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
            RSpec:
         
     | 
| 
       13 
     | 
    
         
            -
              Enabled: true
         
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
            RSpec/MultipleExpectations:
         
     | 
| 
       16 
     | 
    
         
            -
              Enabled: false
         
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
            Performance:
         
     | 
| 
       19 
     | 
    
         
            -
              Enabled: true
         
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
            Bundler:
         
     | 
| 
       22 
     | 
    
         
            -
              Enabled: true
         
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
            Gemspec:
         
     | 
| 
       25 
     | 
    
         
            -
              Enabled: true
         
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
            Style/StringLiterals:
         
     | 
| 
       28 
     | 
    
         
            -
              Enabled: true
         
     | 
| 
       29 
     | 
    
         
            -
              EnforcedStyle: single_quotes
         
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
            Style/FrozenStringLiteralComment:
         
     | 
| 
       32 
     | 
    
         
            -
              Enabled: false
         
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
            Metrics/LineLength:
         
     | 
| 
       35 
     | 
    
         
            -
              Max: 80
         
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
            Metrics/BlockLength:
         
     | 
| 
       38 
     | 
    
         
            -
              Exclude:
         
     | 
| 
       39 
     | 
    
         
            -
                - 'spec/**/*_spec.rb'
         
     | 
| 
       40 
     | 
    
         
            -
                - '**/*.gemspec'
         
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
            Layout/IndentationConsistency:
         
     | 
| 
       43 
     | 
    
         
            -
              EnforcedStyle: normal
         
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
            Style/BlockDelimiters:
         
     | 
| 
       46 
     | 
    
         
            -
              Enabled: true
         
     | 
    
        data/.yardstick.yml
    DELETED
    
    | 
         @@ -1,29 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            ---
         
     | 
| 
       2 
     | 
    
         
            -
            path: ['lib/**/*.rb']
         
     | 
| 
       3 
     | 
    
         
            -
            threshold: 100
         
     | 
| 
       4 
     | 
    
         
            -
            rules:
         
     | 
| 
       5 
     | 
    
         
            -
              ApiTag::Presence:
         
     | 
| 
       6 
     | 
    
         
            -
                enabled: false
         
     | 
| 
       7 
     | 
    
         
            -
              ApiTag::Inclusion:
         
     | 
| 
       8 
     | 
    
         
            -
                enabled: false
         
     | 
| 
       9 
     | 
    
         
            -
              ApiTag::ProtectedMethod:
         
     | 
| 
       10 
     | 
    
         
            -
                enabled: false
         
     | 
| 
       11 
     | 
    
         
            -
              ApiTag::PrivateMethod:
         
     | 
| 
       12 
     | 
    
         
            -
                enabled: false
         
     | 
| 
       13 
     | 
    
         
            -
              ExampleTag:
         
     | 
| 
       14 
     | 
    
         
            -
                enabled: false
         
     | 
| 
       15 
     | 
    
         
            -
              ReturnTag:
         
     | 
| 
       16 
     | 
    
         
            -
                enabled: true
         
     | 
| 
       17 
     | 
    
         
            -
                exclude: []
         
     | 
| 
       18 
     | 
    
         
            -
              Summary::Presence:
         
     | 
| 
       19 
     | 
    
         
            -
                enabled: true
         
     | 
| 
       20 
     | 
    
         
            -
                exclude: []
         
     | 
| 
       21 
     | 
    
         
            -
              Summary::Length:
         
     | 
| 
       22 
     | 
    
         
            -
                enabled: true
         
     | 
| 
       23 
     | 
    
         
            -
                exclude: []
         
     | 
| 
       24 
     | 
    
         
            -
              Summary::Delimiter:
         
     | 
| 
       25 
     | 
    
         
            -
                enabled: true
         
     | 
| 
       26 
     | 
    
         
            -
                exclude: []
         
     | 
| 
       27 
     | 
    
         
            -
              Summary::SingleLine:
         
     | 
| 
       28 
     | 
    
         
            -
                enabled: true
         
     | 
| 
       29 
     | 
    
         
            -
                exclude: []
         
     | 
    
        data/Gemfile
    DELETED
    
    
    
        data/Rakefile
    DELETED
    
    | 
         @@ -1,30 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'bundler/gem_tasks'
         
     | 
| 
       2 
     | 
    
         
            -
            require 'rspec/core/rake_task'
         
     | 
| 
       3 
     | 
    
         
            -
            require 'rubocop/rake_task'
         
     | 
| 
       4 
     | 
    
         
            -
            require 'yaml'
         
     | 
| 
       5 
     | 
    
         
            -
            require 'yardstick'
         
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
            desc('Documentation stats and measurements')
         
     | 
| 
       8 
     | 
    
         
            -
            task('qa:docs') do
         
     | 
| 
       9 
     | 
    
         
            -
              yaml = YAML.load_file(File.expand_path('../.yardstick.yml', __FILE__))
         
     | 
| 
       10 
     | 
    
         
            -
              config = Yardstick::Config.coerce(yaml)
         
     | 
| 
       11 
     | 
    
         
            -
              measure = Yardstick.measure(config)
         
     | 
| 
       12 
     | 
    
         
            -
              measure.puts
         
     | 
| 
       13 
     | 
    
         
            -
              coverage = Yardstick.round_percentage(measure.coverage * 100)
         
     | 
| 
       14 
     | 
    
         
            -
              exit(1) if coverage < config.threshold
         
     | 
| 
       15 
     | 
    
         
            -
            end
         
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
            desc('Codestyle check and linter')
         
     | 
| 
       18 
     | 
    
         
            -
            RuboCop::RakeTask.new('qa:code') do |task|
         
     | 
| 
       19 
     | 
    
         
            -
              task.fail_on_error = true
         
     | 
| 
       20 
     | 
    
         
            -
              task.patterns = [
         
     | 
| 
       21 
     | 
    
         
            -
                'lib/**/*.rb',
         
     | 
| 
       22 
     | 
    
         
            -
                'spec/**/*.rb'
         
     | 
| 
       23 
     | 
    
         
            -
              ]
         
     | 
| 
       24 
     | 
    
         
            -
            end
         
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
            desc('Run CI QA tasks')
         
     | 
| 
       27 
     | 
    
         
            -
            task(qa: ['qa:docs', 'qa:code'])
         
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
            RSpec::Core::RakeTask.new(spec: :qa)
         
     | 
| 
       30 
     | 
    
         
            -
            task(default: :spec)
         
     | 
    
        data/otp-jwt.gemspec
    DELETED
    
    | 
         @@ -1,36 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            lib = File.expand_path('../lib', __FILE__)
         
     | 
| 
       2 
     | 
    
         
            -
            $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
         
     | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
     | 
    
         
            -
            require 'otp/jwt/version'
         
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
            Gem::Specification.new do |spec|
         
     | 
| 
       7 
     | 
    
         
            -
              spec.name          = 'otp-jwt'
         
     | 
| 
       8 
     | 
    
         
            -
              spec.version       = OTP::JWT::VERSION
         
     | 
| 
       9 
     | 
    
         
            -
              spec.authors       = ['Stas Suscov']
         
     | 
| 
       10 
     | 
    
         
            -
              spec.email         = ['stas@nerd.ro']
         
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
              spec.summary       = 'Passwordless HTTP APIs'
         
     | 
| 
       13 
     | 
    
         
            -
              spec.description   = 'OTP (email, SMS) JWT authentication for HTTP APIs.'
         
     | 
| 
       14 
     | 
    
         
            -
              spec.homepage      = 'https://github.com/stas/otp-jwt'
         
     | 
| 
       15 
     | 
    
         
            -
              spec.license       = 'TBD'
         
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
              spec.files         = Dir.chdir(File.expand_path('..', __FILE__)) do
         
     | 
| 
       18 
     | 
    
         
            -
                `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec)/}) }
         
     | 
| 
       19 
     | 
    
         
            -
              end
         
     | 
| 
       20 
     | 
    
         
            -
              spec.require_paths = ['lib']
         
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
              spec.add_dependency 'activesupport'
         
     | 
| 
       23 
     | 
    
         
            -
              spec.add_dependency 'jwt', '~> 2.2.0.pre.beta.0'
         
     | 
| 
       24 
     | 
    
         
            -
              spec.add_dependency 'rotp', '~> 4.1'
         
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
              spec.add_development_dependency 'bundler'
         
     | 
| 
       27 
     | 
    
         
            -
              spec.add_development_dependency 'ffaker'
         
     | 
| 
       28 
     | 
    
         
            -
              spec.add_development_dependency 'rails'
         
     | 
| 
       29 
     | 
    
         
            -
              spec.add_development_dependency 'rspec-rails'
         
     | 
| 
       30 
     | 
    
         
            -
              spec.add_development_dependency 'rubocop-performance'
         
     | 
| 
       31 
     | 
    
         
            -
              spec.add_development_dependency 'rubocop-rails_config'
         
     | 
| 
       32 
     | 
    
         
            -
              spec.add_development_dependency 'rubocop-rspec'
         
     | 
| 
       33 
     | 
    
         
            -
              spec.add_development_dependency 'simplecov'
         
     | 
| 
       34 
     | 
    
         
            -
              spec.add_development_dependency 'sqlite3', ENV['SQLITE3_VERSION']
         
     | 
| 
       35 
     | 
    
         
            -
              spec.add_development_dependency 'yardstick'
         
     | 
| 
       36 
     | 
    
         
            -
            end
         
     |