rockauth 0.0.1.pre2

Sign up to get free protection for your applications and to get access to all the features.
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