rockauth 0.0.1.pre2

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.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +30 -0
  4. data/app/admin/authentication.rb +37 -0
  5. data/app/admin/provider_authentications.rb +24 -0
  6. data/app/admin/resource_owner.rb +79 -0
  7. data/app/controllers/rockauth/authentications_controller.rb +48 -0
  8. data/app/controllers/rockauth/me_controller.rb +93 -0
  9. data/app/controllers/rockauth/provider_authentications_controller.rb +72 -0
  10. data/app/helpers/rockauth/application_helper.rb +4 -0
  11. data/app/models/rockauth/authentication.rb +6 -0
  12. data/app/models/rockauth/provider_authentication.rb +9 -0
  13. data/app/models/rockauth/user.rb +10 -0
  14. data/app/serializers/rockauth/authentication_serializer.rb +24 -0
  15. data/app/serializers/rockauth/base_serializer.rb +6 -0
  16. data/app/serializers/rockauth/error_serializer.rb +5 -0
  17. data/app/serializers/rockauth/provider_authentication_serializer.rb +5 -0
  18. data/app/serializers/rockauth/user_serializer.rb +23 -0
  19. data/app/views/layouts/rockauth/application.html.erb +14 -0
  20. data/config/locales/en.yml +12 -0
  21. data/config/routes.rb +9 -0
  22. data/db/migrate/20150709065335_create_rockauth_users.rb +16 -0
  23. data/db/migrate/20150709071113_create_rockauth_provider_authentications.rb +16 -0
  24. data/db/migrate/20150709084233_create_rockauth_authentications.rb +23 -0
  25. data/lib/generators/rockauth/client_generator.rb +33 -0
  26. data/lib/generators/rockauth/install_generator.rb +59 -0
  27. data/lib/generators/rockauth/migrations_generator.rb +9 -0
  28. data/lib/generators/rockauth/models_generator.rb +11 -0
  29. data/lib/generators/templates/authentication.rb +4 -0
  30. data/lib/generators/templates/provider_authentication.rb +5 -0
  31. data/lib/generators/templates/rockauth_clients.yml +9 -0
  32. data/lib/generators/templates/rockauth_full_initializer.rb +41 -0
  33. data/lib/generators/templates/rockauth_providers.json +50 -0
  34. data/lib/generators/templates/user.rb +7 -0
  35. data/lib/rockauth.rb +15 -0
  36. data/lib/rockauth/authenticator.rb +51 -0
  37. data/lib/rockauth/authenticator/response.rb +32 -0
  38. data/lib/rockauth/client.rb +4 -0
  39. data/lib/rockauth/configuration.rb +51 -0
  40. data/lib/rockauth/controllers.rb +5 -0
  41. data/lib/rockauth/controllers/authentication.rb +36 -0
  42. data/lib/rockauth/engine.rb +15 -0
  43. data/lib/rockauth/errors.rb +18 -0
  44. data/lib/rockauth/models.rb +9 -0
  45. data/lib/rockauth/models/authentication.rb +151 -0
  46. data/lib/rockauth/models/provider_authentication.rb +59 -0
  47. data/lib/rockauth/models/provider_validation.rb +61 -0
  48. data/lib/rockauth/models/resource_owner.rb +31 -0
  49. data/lib/rockauth/models/user.rb +25 -0
  50. data/lib/rockauth/provider_user_information.rb +103 -0
  51. data/lib/rockauth/version.rb +3 -0
  52. data/lib/tasks/rockauth_tasks.rake +9 -0
  53. metadata +361 -0
@@ -0,0 +1,32 @@
1
+ module Rockauth
2
+ class Authenticator::Response < Struct.new(:success, :resource_owner, :authentication)
3
+ alias_method :success?, :success
4
+
5
+ def apply
6
+ self.success = authentication.save
7
+ self.resource_owner = authentication.resource_owner # owner is set before_validation
8
+ end
9
+
10
+ def error
11
+ unless success
12
+ @error ||= Errors::ControllerError.new 400, I18n.t("rockauth.errors.authentication_failed"), authentication.try(:errors).as_json
13
+ end
14
+ end
15
+
16
+ def render
17
+ if success?
18
+ render_success
19
+ else
20
+ render_error
21
+ end
22
+ end
23
+
24
+ def render_success
25
+ { json: authentication, status: 200 }
26
+ end
27
+
28
+ def render_error
29
+ Rockauth::Configuration.error_renderer.call(error)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,4 @@
1
+ module Rockauth
2
+ Client = Struct.new(:id, :secret, :title) do
3
+ end
4
+ end
@@ -0,0 +1,51 @@
1
+ module Rockauth
2
+ Configuration = Struct.new(*%i(allowed_password_length email_regexp token_time_to_live clients
3
+ resource_owner_class warn_missing_social_auth_gems providers jwt
4
+ serializers generate_active_admin_resources active_admin_menu_name error_renderer)) do
5
+ def resource_owner_class= arg
6
+ @constantized_resource_owner_class = nil
7
+ @resource_owner_class = arg
8
+ end
9
+ def resource_owner_class
10
+ @constantized_resource_owner_class ||= (@resource_owner_class.respond_to?(:constantize) ? @resource_owner_class.constantize : @resource_owner_class)
11
+ end
12
+ end.new.tap do |config|
13
+ config.allowed_password_length = 8..72
14
+ config.email_regexp = /\A[^@\s]+@([^@\s]+\.)+[^@\W]+\z/
15
+ config.token_time_to_live = 365 * 24 * 60 * 60
16
+ config.clients = []
17
+ config.resource_owner_class = 'Rockauth::User'
18
+ config.warn_missing_social_auth_gems = true
19
+
20
+ config.providers = Struct.new(*%i(twitter instagram google_plus)).new.tap do |providers|
21
+ %i(twitter instagram google_plus).each do |provider|
22
+ providers.public_send("#{provider}=", {})
23
+ end
24
+ end
25
+
26
+ config.jwt = Struct.new(*%i(secret issuer signing_method)).new.tap do |jwt_config|
27
+ jwt_config.secret = ''
28
+ jwt_config.issuer = ''
29
+ jwt_config.signing_method = 'HS256'
30
+ end
31
+
32
+ config.serializers = Struct.new(*%i(error user authentication provider_authentication)).new.tap do |serializers|
33
+ serializers.error = "Rockauth::ErrorSerializer"
34
+ serializers.user = "Rockauth::UserSerializer"
35
+ serializers.authentication = "Rockauth::AuthenticationSerializer"
36
+ serializers.provider_authentication = "Rockauth::ProviderAuthenticationSerializer"
37
+ end
38
+
39
+ config.generate_active_admin_resources = nil
40
+ config.active_admin_menu_name = 'Authentication'
41
+
42
+ config.error_renderer = -> error do
43
+ { json: error, serializer: Rockauth::Configuration.serializers.error.safe_constantize, status: error.status_code }
44
+ end
45
+ end
46
+
47
+ def self.configure
48
+ yield Configuration if block_given?
49
+ Configuration
50
+ end
51
+ end
@@ -0,0 +1,5 @@
1
+ module Rockauth
2
+ module Controllers
3
+ autoload :Authentication, 'rockauth/controllers/authentication'
4
+ end
5
+ end
@@ -0,0 +1,36 @@
1
+ module Rockauth
2
+ module Controllers::Authentication
3
+ extend ActiveSupport::Concern
4
+
5
+ def render_error status_code=500, message=I18n.t("rockauth.errors.server_error"), validation_errors=nil
6
+ error = Errors::ControllerError.new(status_code, message, validation_errors)
7
+ render Rockauth::Configuration.error_renderer.call(error)
8
+ end
9
+
10
+ def render_unauthorized
11
+ render_error 401, I18n.t("rockauth.errors.unauthorized")
12
+ end
13
+
14
+ def authenticate_resource_owner!
15
+ render_unauthorized unless current_authentication.try(:active?)
16
+ end
17
+
18
+ def current_resource_owner
19
+ @current_resource_owner ||= current_authentication.try(:resource_owner)
20
+ end
21
+
22
+ def current_authentication
23
+ if @_authentication_checked
24
+ @current_authentication
25
+ else
26
+ @_authentication_checked = true
27
+ @current_authentication = Authenticator.verified_authentication_for_request request, self
28
+ end
29
+ end
30
+ included do
31
+ include ActionController::Helpers # TODO: we dont want to force apis to have helpers if we can avoid it.....
32
+ helper_method :current_resource_owner
33
+ helper_method :current_authentication
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,15 @@
1
+ require 'rails-api'
2
+
3
+ module Rockauth
4
+ class Engine < ::Rails::Engine
5
+ initializer "rockauth.inject.controller_concern" do
6
+ ActionController::API.send :include, Rockauth::Controllers::Authentication
7
+ end
8
+
9
+ initializer "rockauth.load_active_admin_resources" do
10
+ if Rockauth::Configuration.generate_active_admin_resources || Rockauth::Configuration.generate_active_admin_resources.nil? && defined?(ActiveAdmin)
11
+ ActiveAdmin.application.load_paths += [File.expand_path(File.dirname(__FILE__) + '../../../app/admin')]
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ require 'active_model'
2
+
3
+ module Rockauth
4
+ module Errors
5
+ ControllerError = Struct.new(:status_code, :message, :validation_errors) do
6
+ extend ActiveModel::Naming
7
+ include ActiveModel::Serialization
8
+
9
+ def self.model_name
10
+ 'Error'
11
+ end
12
+
13
+ def self.active_model_serializer
14
+ Rockauth::Configuration.serializers.error.safe_constantize
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,9 @@
1
+ module Rockauth
2
+ module Models
3
+ autoload :Authentication, 'rockauth/models/authentication'
4
+ autoload :ProviderAuthentication, 'rockauth/models/provider_authentication'
5
+ autoload :ProviderValidation, 'rockauth/models/provider_validation'
6
+ autoload :ResourceOwner, 'rockauth/models/resource_owner'
7
+ autoload :User, 'rockauth/models/user'
8
+ end
9
+ end
@@ -0,0 +1,151 @@
1
+ require 'jwt'
2
+
3
+ module Rockauth
4
+ module Models::Authentication
5
+ extend ActiveSupport::Concern
6
+
7
+ def client
8
+ @client ||= if client_id.present?
9
+ Configuration.clients.find { |c| c.id == client_id }
10
+ end
11
+ end
12
+
13
+ def password?
14
+ auth_type == 'password'
15
+ end
16
+
17
+ def assertion?
18
+ auth_type == 'assertion'
19
+ end
20
+
21
+ def registration?
22
+ auth_type == 'registration'
23
+ end
24
+
25
+ def time_to_live= t
26
+ @time_to_live = t
27
+ end
28
+
29
+ def time_to_live
30
+ @time_to_live ||= Configuration.token_time_to_live
31
+ end
32
+
33
+ def generate_token_id
34
+ self.token_id ||= SecureRandom.base64(24)
35
+ end
36
+
37
+ def hash_token_id
38
+ self.hashed_token_id ||= self.class.hash_token_id token_id
39
+ end
40
+
41
+ def active?
42
+ Time.at(expiration) > Time.now
43
+ end
44
+
45
+ def valid_payload? payload
46
+ active? && payload['iat'] == issued_at && payload['exp'] == expiration
47
+ end
48
+
49
+ def generate_token
50
+ self.token ||= JWT.encode jwt_payload, Configuration.jwt.secret, Configuration.jwt.signing_method
51
+ end
52
+
53
+ def jwt_payload
54
+ {
55
+ iss: Configuration.jwt.issuer,
56
+ iat: issued_at,
57
+ exp: expiration,
58
+ aud: client_id,
59
+ sub: resource_owner_id,
60
+ jti: token_id
61
+ }
62
+ end
63
+
64
+ module ClassMethods
65
+ def rockauth_authentication include_associations: true, provider_authentication_class_name: "Rockauth::ProviderAuthentication"
66
+ if include_associations
67
+ belongs_to :resource_owner, polymorphic: true, inverse_of: :authentications
68
+ belongs_to :provider_authentication, class_name: provider_authentication_class_name
69
+
70
+ accepts_nested_attributes_for :provider_authentication
71
+ end
72
+
73
+ scope :expired, -> { where('expiration <= ?', Time.now.to_i) }
74
+ scope :unexpired, -> { where('expiration > ?', Time.now.to_i) }
75
+
76
+ %i(resource_owner_class password username token_id token client_secret).each do |key|
77
+ attr_accessor key
78
+ end
79
+
80
+ validates_presence_of :auth_type
81
+ validates_inclusion_of :auth_type, in: %w(password assertion registration)
82
+ validates_presence_of :client_id
83
+ validates_presence_of :client_secret, on: :create
84
+ validates_presence_of :resource_owner
85
+ validates_presence_of :expiration
86
+ validates_presence_of :issued_at
87
+ validates_presence_of :auth_type
88
+
89
+ before_validation on: :create do
90
+ self.expiration ||= Time.now.to_i + time_to_live
91
+ self.issued_at ||= Time.now.to_i
92
+
93
+ if password?
94
+ self.resource_owner = resource_owner_class.with_username(username).first
95
+ elsif assertion?
96
+ build_provider_authentication unless self.provider_authentication.present?
97
+ provider_authentication.authentication = self
98
+ self.provider_authentication = provider_authentication.exchange
99
+ self.resource_owner = provider_authentication.resource_owner
100
+ end
101
+
102
+ true
103
+ end
104
+
105
+ validates_presence_of :username, if: :password?, on: :create
106
+ validates_presence_of :password, if: :password?, on: :create
107
+ validate on: :create, if: :password? do
108
+ if resource_owner.present? && !resource_owner.authenticate(password)
109
+ errors.add :password, :invalid
110
+ end
111
+ end
112
+
113
+ validates_presence_of :provider_authentication, if: :assertion?, on: :create
114
+
115
+ validate on: :create do
116
+ if client.blank?
117
+ errors.add :client_id, :invalid
118
+ elsif client.secret != client_secret
119
+ errors.add :client_secret, :invalid
120
+ end
121
+ end
122
+
123
+ before_create do
124
+ generate_token_id
125
+ generate_token
126
+ hash_token_id
127
+ true
128
+ end
129
+
130
+ class << self
131
+ def for_token token
132
+ begin
133
+ payload = JWT.decode(token, Configuration.jwt.secret).first
134
+ authentication = where(hashed_token_id: hash_token_id(payload['jti'])).first
135
+ authentication if authentication.present? && authentication.valid_payload?(payload)
136
+ rescue JWT::VerificationError => e
137
+ Rails.logger.error "[Rockauth] Possible Forgery Attempt: #{e}"
138
+ nil
139
+ rescue JWT::DecodeError
140
+ nil
141
+ end
142
+ end
143
+
144
+ def hash_token_id jti
145
+ Digest::SHA2.hexdigest jti
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,59 @@
1
+ module Rockauth
2
+ module Models::ProviderAuthentication
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def provider_authentication include_associations: true, authentication_class_name: 'Rockauth::Authentication'
7
+
8
+ if include_associations
9
+ belongs_to :resource_owner, polymorphic: true, inverse_of: :provider_authentications
10
+ has_many :authentications, class_name: authentication_class_name, dependent: :nullify
11
+ end
12
+
13
+ attr_accessor :authentication
14
+
15
+ validates_presence_of :resource_owner
16
+ validates_uniqueness_of :provider_user_id, scope: :provider
17
+ validates_presence_of :provider
18
+ validates_presence_of :provider_user_id
19
+ validates_presence_of :provider_access_token
20
+
21
+ validate :validate_attributes_unchangable
22
+
23
+ delegate :resource_owner_class, to: :authentication
24
+
25
+ define_method :validate_attributes_unchangable do
26
+ %i(resource_owner_id resource_owner_type provider provider_user_id).each do |key|
27
+ errors.add key, :rockauth_cannot_be_changed if !new_record? && public_send(:"#{key}_changed?")
28
+ end
29
+ end
30
+
31
+ define_method :exchange do
32
+ result = self
33
+
34
+ configure_from_provider
35
+
36
+ if provider_user_id.present? && provider.present?
37
+ result = self.class.where(provider: provider, provider_user_id: provider_user_id).first
38
+ if result.present?
39
+ result.provider_user_information = provider_user_information
40
+ result.assign_attributes_from_provider
41
+ result
42
+ else
43
+ handle_missing_resource_owner_on_valid_assertion
44
+ result = self
45
+ end
46
+ end
47
+
48
+ result
49
+ end
50
+
51
+ define_method :handle_missing_resource_owner_on_valid_assertion do
52
+ self.resource_owner = resource_owner_class.new
53
+ resource_owner.assign_attributes_from_provider_user(provider_user_information)
54
+ resource_owner.provider_authentications << self
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,61 @@
1
+ module Rockauth
2
+ module Models::ProviderValidation
3
+ extend ActiveSupport::Concern
4
+ attr_accessor :provider_user_information
5
+
6
+ included do
7
+ validates_inclusion_of :provider, in: ->(instance) { instance.class.valid_networks }
8
+ attr_accessor :skip_provider_authentication
9
+ before_validation :configure_from_provider, unless: :skip_provider_authentication
10
+
11
+ def configure_from_provider
12
+ if provider.present? && self.class.valid_networks.include?(provider)
13
+ self.provider_user_information = instance_exec &self.class.network_validator(provider)
14
+ assign_attributes_from_provider
15
+ end
16
+
17
+ true
18
+ end
19
+
20
+ def assign_attributes_from_provider
21
+ return unless provider_user_information.present?
22
+ self.provider_user_id = provider_user_information.user_id
23
+ end
24
+
25
+ { facebook: 'fb_graph2', instagram: 'instagram', twitter: 'twitter', google_plus: nil }.each do |key, value|
26
+ begin
27
+ require value if value.present?
28
+ provider_network key do
29
+ ProviderUserInformation.for_provider(provider, provider_access_token, provider_access_token_secret)
30
+ end
31
+ rescue LoadError
32
+ if Rockauth::Configuration.warn_missing_social_auth_gems
33
+ warn "Rockauth: Could not load the #{value} gem, #{key.to_s.humanize} provider authentication disabled"
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+
40
+ module ClassMethods
41
+ def inherited subclass
42
+ super
43
+ subclass.instance_variable_set :@network_validator_configuration, (@network_validator_configuration || {}).dup
44
+ end
45
+
46
+ def provider_network provider, &block
47
+ fail ArgumentError, "no block given" unless block_given?
48
+ @network_validator_configuration ||= {}
49
+ @network_validator_configuration[provider.to_s] = block
50
+ end
51
+
52
+ def valid_networks
53
+ @network_validator_configuration.keys
54
+ end
55
+
56
+ def network_validator provider
57
+ @network_validator_configuration[provider.to_s]
58
+ end
59
+ end
60
+ end
61
+ end