solidus_jwt 0.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +35 -0
  3. data/.gem_release.yml +5 -0
  4. data/.github/CODEOWNERS +4 -0
  5. data/.github/stale.yml +17 -0
  6. data/.gitignore +19 -0
  7. data/.rspec +1 -0
  8. data/.rubocop.yml +16 -0
  9. data/.ruby-gemset +1 -0
  10. data/.ruby-version +1 -0
  11. data/Gemfile +33 -0
  12. data/README.md +71 -2
  13. data/Rakefile +4 -28
  14. data/_config.yml +1 -0
  15. data/app/decorators/controllers/solidus_jwt/spree/api/base_controller_decorator.rb +41 -0
  16. data/app/decorators/models/solidus_jwt/spree/user_decorator.rb +58 -0
  17. data/app/models/solidus_jwt/base_record.rb +13 -0
  18. data/app/models/solidus_jwt/token.rb +63 -0
  19. data/bin/console +17 -0
  20. data/bin/r +9 -0
  21. data/bin/rails +8 -0
  22. data/bin/rails-engine +15 -0
  23. data/bin/rails-sandbox +17 -0
  24. data/bin/rake +7 -0
  25. data/bin/sandbox +84 -0
  26. data/bin/sandbox_rails +9 -0
  27. data/bin/setup +8 -0
  28. data/config/locales/en.yml +2 -1
  29. data/config/routes.rb +3 -0
  30. data/db/migrate/20190222220038_create_solidus_jwt_tokens.rb +14 -0
  31. data/db/migrate/20191212083655_add_foreign_key_to_users_table.rb +7 -0
  32. data/lib/controllers/api/spree/api/oauths_controller.rb +47 -0
  33. data/lib/generators/solidus_jwt/install/install_generator.rb +3 -11
  34. data/lib/solidus_jwt.rb +8 -0
  35. data/lib/solidus_jwt/concerns/decodeable.rb +7 -1
  36. data/lib/solidus_jwt/concerns/encodeable.rb +14 -2
  37. data/lib/solidus_jwt/config.rb +2 -0
  38. data/lib/solidus_jwt/devise_strategies/base.rb +25 -0
  39. data/lib/solidus_jwt/devise_strategies/password.rb +46 -0
  40. data/lib/solidus_jwt/devise_strategies/refresh_token.rb +48 -0
  41. data/lib/solidus_jwt/distributor/devise.rb +3 -1
  42. data/lib/solidus_jwt/engine.rb +8 -12
  43. data/lib/solidus_jwt/factories.rb +13 -0
  44. data/lib/solidus_jwt/preferences.rb +8 -0
  45. data/lib/solidus_jwt/version.rb +3 -9
  46. data/solidus_jwt.gemspec +38 -0
  47. data/spec/lib/solidus_jwt/concerns/decodeable_spec.rb +0 -0
  48. data/spec/lib/solidus_jwt/concerns/encodeable_spec.rb +0 -0
  49. data/spec/lib/solidus_jwt/config_spec.rb +7 -0
  50. data/spec/lib/solidus_jwt/devise_strategies/password_spec.rb +78 -0
  51. data/spec/lib/solidus_jwt/devise_strategies/refresh_token_spec.rb +74 -0
  52. data/spec/lib/solidus_jwt/preferences_spec.rb +39 -0
  53. data/spec/lib/solidus_jwt_spec.rb +8 -0
  54. data/spec/models/solidus_jwt/token_spec.rb +43 -0
  55. data/spec/requests/spree/api/json_web_tokens_spec.rb +77 -0
  56. data/spec/requests/spree/api/oauths_spec.rb +122 -0
  57. data/spec/spec_helper.rb +26 -0
  58. data/spec/support/shared_examples/decodeable_examples.rb +23 -0
  59. data/spec/support/shared_examples/encodeable_examples.rb +29 -0
  60. metadata +86 -222
  61. data/app/assets/javascripts/spree/backend/solidus_jwt.js +0 -2
  62. data/app/assets/javascripts/spree/frontend/solidus_jwt.js +0 -2
  63. data/app/assets/stylesheets/spree/backend/solidus_jwt.css +0 -4
  64. data/app/assets/stylesheets/spree/frontend/solidus_jwt.css +0 -4
  65. data/app/controllers/spree/api/base_controller/json_web_tokens.rb +0 -22
  66. data/app/controllers/spree/api/base_controller_decorator.rb +0 -17
  67. data/app/models/spree/user_decorator.rb +0 -30
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require "bundler/setup"
6
+ require "solidus_jwt"
7
+
8
+ # You can add fixtures and/or initialization code here to make experimenting
9
+ # with your gem easier. You can also use a different console, if you like.
10
+ $LOAD_PATH.unshift(*Dir["#{__dir__}/../app/*"])
11
+
12
+ # (If you use this, don't forget to add pry to your Gemfile!)
13
+ # require "pry"
14
+ # Pry.start
15
+
16
+ require "irb"
17
+ IRB.start(__FILE__)
data/bin/r ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ warn %{
5
+ DEPRECATION: bin/r has been replaced by bin/rails-engine, please use that
6
+ command instead.
7
+ }.strip
8
+
9
+ exec "#{__dir__}/rails-engine"
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ if %w[g generate].include? ARGV.first
5
+ exec "#{__dir__}/rails-engine", *ARGV
6
+ else
7
+ exec "#{__dir__}/rails-sandbox", *ARGV
8
+ end
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # This command will automatically be run when you run "rails" with Rails gems
5
+ # installed from the root of your application.
6
+
7
+ ENGINE_ROOT = File.expand_path('..', __dir__)
8
+ ENGINE_PATH = File.expand_path('../lib/solidus_jwt/engine', __dir__)
9
+
10
+ # Set up gems listed in the Gemfile.
11
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
12
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
13
+
14
+ require 'rails/all'
15
+ require 'rails/engine/commands'
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ app_root = 'sandbox'
5
+
6
+ unless File.exist? "#{app_root}/bin/rails"
7
+ warn 'Creating the sandbox app...'
8
+ Dir.chdir "#{__dir__}/.." do
9
+ system "#{__dir__}/sandbox" or begin # rubocop:disable Style/AndOr
10
+ warn 'Automatic creation of the sandbox app failed'
11
+ exit 1
12
+ end
13
+ end
14
+ end
15
+
16
+ Dir.chdir app_root
17
+ exec 'bin/rails', *ARGV
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "rubygems"
5
+ require "bundler/setup"
6
+
7
+ load Gem.bin_path("rake", "rake")
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ case "$DB" in
6
+ postgres|postgresql)
7
+ RAILSDB="postgresql"
8
+ ;;
9
+ mysql)
10
+ RAILSDB="mysql"
11
+ ;;
12
+ sqlite|'')
13
+ RAILSDB="sqlite3"
14
+ ;;
15
+ *)
16
+ echo "Invalid DB specified: $DB"
17
+ exit 1
18
+ ;;
19
+ esac
20
+
21
+ if [ ! -z $SOLIDUS_BRANCH ]
22
+ then
23
+ BRANCH=$SOLIDUS_BRANCH
24
+ else
25
+ BRANCH="master"
26
+ fi
27
+
28
+ extension_name="solidus_jwt"
29
+
30
+ # Stay away from the bundler env of the containing extension.
31
+ function unbundled {
32
+ ruby -rbundler -e'b = proc {system *ARGV}; Bundler.respond_to?(:with_unbundled_env) ? Bundler.with_unbundled_env(&b) : Bundler.with_clean_env(&b)' -- $@
33
+ }
34
+
35
+ rm -rf ./sandbox
36
+ unbundled bundle exec rails new sandbox --database="$RAILSDB" \
37
+ --skip-bundle \
38
+ --skip-git \
39
+ --skip-keeps \
40
+ --skip-rc \
41
+ --skip-spring \
42
+ --skip-test \
43
+ --skip-javascript
44
+
45
+ if [ ! -d "sandbox" ]; then
46
+ echo 'sandbox rails application failed'
47
+ exit 1
48
+ fi
49
+
50
+ cd ./sandbox
51
+ cat <<RUBY >> Gemfile
52
+ gem 'solidus', github: 'solidusio/solidus', branch: '$BRANCH'
53
+ gem 'solidus_auth_devise', '>= 2.1.0'
54
+ gem 'rails-i18n'
55
+ gem 'solidus_i18n'
56
+
57
+ gem '$extension_name', path: '..'
58
+
59
+ group :test, :development do
60
+ platforms :mri do
61
+ gem 'pry-byebug'
62
+ end
63
+ end
64
+ RUBY
65
+
66
+ unbundled bundle install --gemfile Gemfile
67
+
68
+ unbundled bundle exec rake db:drop db:create
69
+
70
+ unbundled bundle exec rails generate spree:install \
71
+ --auto-accept \
72
+ --user_class=Spree::User \
73
+ --enforce_available_locales=true \
74
+ --with-authentication=false \
75
+ $@
76
+
77
+ unbundled bundle exec rails generate solidus:auth:install
78
+
79
+ echo
80
+ echo "🚀 Sandbox app successfully created for $extension_name!"
81
+ echo "🚀 Using $RAILSDB and Solidus $BRANCH"
82
+ echo "🚀 Use 'export DB=[postgres|mysql|sqlite]' to control the DB adapter"
83
+ echo "🚀 Use 'export SOLIDUS_BRANCH=<BRANCH-NAME>' to control the Solidus version"
84
+ echo "🚀 This app is intended for test purposes."
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ warn %{
5
+ DEPRECATION: bin/sandbox_rails has been replaced by bin/rails-sandbox, please
6
+ use that command instead.
7
+ }.strip
8
+
9
+ exec "#{__dir__}/rails-engine"
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ gem install bundler --conservative
7
+ bundle update
8
+ bin/rake clobber
@@ -2,4 +2,5 @@
2
2
  # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3
3
 
4
4
  en:
5
- hello: Hello world
5
+ solidus_jwt:
6
+ invalid_credentials: invalid username or password
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Spree::Core::Engine.routes.draw do
2
4
  # Add your extension routes here
5
+ post 'oauth/token', to: 'api/oauths#token'
3
6
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateSolidusJwtTokens < ActiveRecord::Migration[5.2]
4
+ def change
5
+ create_table :solidus_jwt_tokens do |t|
6
+ t.string :token, index: true
7
+ t.integer :auth_type, default: 0, null: false
8
+ t.integer :user_id, index: true
9
+ t.boolean :active, default: true, null: false
10
+
11
+ t.timestamps null: false
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddForeignKeyToUsersTable < ActiveRecord::Migration[5.2]
4
+ def change
5
+ add_foreign_key :solidus_jwt_tokens, Spree.user_class.table_name, column: :user_id, on_delete: :cascade
6
+ end
7
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ module Api
5
+ class OauthsController < BaseController
6
+ skip_before_action :authenticate_user
7
+
8
+ def token
9
+ result = catch(:warden) do
10
+ try_authenticate_user
11
+ end
12
+
13
+ case result
14
+ when Spree::User
15
+ render json: token_response_json(result)
16
+ when Hash
17
+ render status: :unauthorized, json: { error: I18n.t(result[:message], scope: 'devise.failure') }
18
+ else
19
+ render status: :unauthorized, json: { error: I18n.t(:invalid_credentials, scope: 'solidus_jwt') }
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def token_response_json(user)
26
+ expires_in = SolidusJwt::Config.jwt_expiration
27
+
28
+ {
29
+ token_type: 'bearer',
30
+ access_token: user.generate_jwt(expires_in: expires_in),
31
+ expires_in: expires_in,
32
+ refresh_token: generate_refresh_token_for(user)
33
+ }
34
+ end
35
+
36
+ def try_authenticate_user
37
+ warden.authenticate(:solidus_jwt_password) ||
38
+ warden.authenticate(:solidus_jwt_refresh_token)
39
+ end
40
+
41
+ def generate_refresh_token_for(user)
42
+ token_resource = user.auth_tokens.create!
43
+ token_resource.token
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,18 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SolidusJwt
2
4
  module Generators
3
5
  class InstallGenerator < Rails::Generators::Base
4
6
  class_option :auto_run_migrations, type: :boolean, default: false
5
7
 
6
- def add_javascripts
7
- append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require spree/frontend/solidus_jwt\n"
8
- append_file 'vendor/assets/javascripts/spree/backend/all.js', "//= require spree/backend/solidus_jwt\n"
9
- end
10
-
11
- def add_stylesheets
12
- inject_into_file 'vendor/assets/stylesheets/spree/frontend/all.css', " *= require spree/frontend/solidus_jwt\n", before: /\*\//, verbose: true
13
- inject_into_file 'vendor/assets/stylesheets/spree/backend/all.css', " *= require spree/backend/solidus_jwt\n", before: /\*\//, verbose: true
14
- end
15
-
16
8
  def add_migrations
17
9
  run 'bundle exec rake railties:install:migrations FROM=solidus_jwt'
18
10
  end
@@ -22,7 +14,7 @@ module SolidusJwt
22
14
  if run_migrations
23
15
  run 'bundle exec rake db:migrate'
24
16
  else
25
- puts 'Skipping rake db:migrate, don\'t forget to run it!'
17
+ puts 'Skipping rake db:migrate, don\'t forget to run it!' # rubocop:disable Rails/Output
26
18
  end
27
19
  end
28
20
  end
@@ -1,8 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'jwt'
2
4
 
3
5
  require 'solidus_core'
6
+ require 'solidus_support'
7
+ require 'solidus_auth_devise'
4
8
  require 'solidus_jwt/engine'
5
9
 
10
+ require 'solidus_jwt/devise_strategies/base'
11
+ require 'solidus_jwt/devise_strategies/password'
12
+ require 'solidus_jwt/devise_strategies/refresh_token'
13
+
6
14
  require 'solidus_jwt/version'
7
15
  require 'solidus_jwt/config'
8
16
  require 'solidus_jwt/concerns/decodeable'
@@ -1,15 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SolidusJwt
2
4
  module Decodeable
3
5
  ##
4
6
  # Decode a token generated by SolidusJwt
5
7
  # @see https://github.com/jwt/ruby-jwt
6
8
  #
9
+ # @example decode a token.
10
+ # SolidusJwt.decode('abc.123.efg')
11
+ # #=> [{"sub"=>"1234567890", "name"=>"John Doe", "iat"=>1516239022}, {"alg"=>"HS256", "typ"=>"JWT"}]
12
+ #
7
13
  # @param token [String] The token to decode
8
14
  # @return [Array<Hash>]
9
15
  #
10
16
  def decode(token)
11
17
  JWT.decode(token, SolidusJwt::Config.jwt_secret, true,
12
- algorithm: SolidusJwt::Config.jwt_algorithm)
18
+ algorithm: SolidusJwt::Config.jwt_algorithm)
13
19
  end
14
20
  end
15
21
  end
@@ -1,9 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SolidusJwt
2
4
  module Encodeable
3
5
  ##
4
6
  # Encode a specified payload
5
7
  # @see https://github.com/jwt/ruby-jwt
6
8
  #
9
+ # @example encode data into token
10
+ # payload = {
11
+ # sub: 1,
12
+ # iat: DateTime.current.to_i,
13
+ # exp: 1.hour.from_now.to_i
14
+ # }
15
+ #
16
+ # SolidusJwt.encode payload: payload
17
+ # #=> 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImlhdCI6MTU4NDEzMjExOCwiZXhwIjoxNTg0MTM1NzE4LCJpc3MiOiJzb2xpZHVzIn0.OKZOGlawx435GdgKp2AGD8SKxW7sqn0h-Ef2qdVSxqQ'
18
+ #
7
19
  # @param payload [Hash] Attributes to place within the jwt
8
20
  # @param expires_in [Integer] How long until token expires in Seconds (*Optional*).
9
21
  # Note that if no expires at is set, then the token will last forever.
@@ -15,12 +27,12 @@ module SolidusJwt
15
27
  current_time = Time.current.to_i
16
28
 
17
29
  # @see https://github.com/jwt/ruby-jwt#support-for-reserved-claim-names
18
- jwt_payload[:exp] ||= current_time + expires_in if expires_in.present?
30
+ jwt_payload[:exp] ||= current_time + expires_in.to_i if expires_in.present?
19
31
  jwt_payload[:iat] ||= current_time
20
32
  jwt_payload[:iss] ||= 'solidus'
21
33
 
22
34
  JWT.encode(jwt_payload, SolidusJwt::Config.jwt_secret,
23
- SolidusJwt::Config.jwt_algorithm)
35
+ SolidusJwt::Config.jwt_algorithm)
24
36
  end
25
37
  end
26
38
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'solidus_jwt/preferences'
2
4
 
3
5
  SolidusJwt::Config = SolidusJwt::Preferences.new
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusJwt
4
+ module DeviseStrategies
5
+ class Base < Devise::Strategies::Authenticatable
6
+ def valid?
7
+ valid_grant_type? && valid_params?
8
+ end
9
+
10
+ private
11
+
12
+ def grant_type
13
+ params[:grant_type]
14
+ end
15
+
16
+ def valid_grant_type?
17
+ raise NotImplementedError
18
+ end
19
+
20
+ def valid_params?
21
+ raise NotImplementedError
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusJwt
4
+ module DeviseStrategies
5
+ class Password < Base
6
+ def authenticate!
7
+ block = proc { resource.valid_password?(password) }
8
+
9
+ if resource&.valid_for_authentication?(&block)
10
+ resource.after_database_authentication
11
+ return success!(resource)
12
+ end
13
+
14
+ fail!(:invalid)
15
+ end
16
+
17
+ private
18
+
19
+ def resource
20
+ @resource ||= mapping.to.find_for_database_authentication(auth_hash)
21
+ end
22
+
23
+ def auth_hash
24
+ { email: username }
25
+ end
26
+
27
+ def username
28
+ params[:username]
29
+ end
30
+
31
+ def password
32
+ params[:password]
33
+ end
34
+
35
+ def valid_grant_type?
36
+ grant_type == 'password'
37
+ end
38
+
39
+ def valid_params?
40
+ username.present? && password.present?
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ Warden::Strategies.add(:solidus_jwt_password, SolidusJwt::DeviseStrategies::Password)