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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +30 -0
- data/app/admin/authentication.rb +37 -0
- data/app/admin/provider_authentications.rb +24 -0
- data/app/admin/resource_owner.rb +79 -0
- data/app/controllers/rockauth/authentications_controller.rb +48 -0
- data/app/controllers/rockauth/me_controller.rb +93 -0
- data/app/controllers/rockauth/provider_authentications_controller.rb +72 -0
- data/app/helpers/rockauth/application_helper.rb +4 -0
- data/app/models/rockauth/authentication.rb +6 -0
- data/app/models/rockauth/provider_authentication.rb +9 -0
- data/app/models/rockauth/user.rb +10 -0
- data/app/serializers/rockauth/authentication_serializer.rb +24 -0
- data/app/serializers/rockauth/base_serializer.rb +6 -0
- data/app/serializers/rockauth/error_serializer.rb +5 -0
- data/app/serializers/rockauth/provider_authentication_serializer.rb +5 -0
- data/app/serializers/rockauth/user_serializer.rb +23 -0
- data/app/views/layouts/rockauth/application.html.erb +14 -0
- data/config/locales/en.yml +12 -0
- data/config/routes.rb +9 -0
- data/db/migrate/20150709065335_create_rockauth_users.rb +16 -0
- data/db/migrate/20150709071113_create_rockauth_provider_authentications.rb +16 -0
- data/db/migrate/20150709084233_create_rockauth_authentications.rb +23 -0
- data/lib/generators/rockauth/client_generator.rb +33 -0
- data/lib/generators/rockauth/install_generator.rb +59 -0
- data/lib/generators/rockauth/migrations_generator.rb +9 -0
- data/lib/generators/rockauth/models_generator.rb +11 -0
- data/lib/generators/templates/authentication.rb +4 -0
- data/lib/generators/templates/provider_authentication.rb +5 -0
- data/lib/generators/templates/rockauth_clients.yml +9 -0
- data/lib/generators/templates/rockauth_full_initializer.rb +41 -0
- data/lib/generators/templates/rockauth_providers.json +50 -0
- data/lib/generators/templates/user.rb +7 -0
- data/lib/rockauth.rb +15 -0
- data/lib/rockauth/authenticator.rb +51 -0
- data/lib/rockauth/authenticator/response.rb +32 -0
- data/lib/rockauth/client.rb +4 -0
- data/lib/rockauth/configuration.rb +51 -0
- data/lib/rockauth/controllers.rb +5 -0
- data/lib/rockauth/controllers/authentication.rb +36 -0
- data/lib/rockauth/engine.rb +15 -0
- data/lib/rockauth/errors.rb +18 -0
- data/lib/rockauth/models.rb +9 -0
- data/lib/rockauth/models/authentication.rb +151 -0
- data/lib/rockauth/models/provider_authentication.rb +59 -0
- data/lib/rockauth/models/provider_validation.rb +61 -0
- data/lib/rockauth/models/resource_owner.rb +31 -0
- data/lib/rockauth/models/user.rb +25 -0
- data/lib/rockauth/provider_user_information.rb +103 -0
- data/lib/rockauth/version.rb +3 -0
- data/lib/tasks/rockauth_tasks.rake +9 -0
- 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,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,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
|