Skip to content

Commit

Permalink
Merge pull request #2 from IhorKobka/multiple-models-auth
Browse files Browse the repository at this point in the history
Multiple models auth
  • Loading branch information
IhorKobka authored Jul 5, 2019
2 parents c795930 + b1b7ad6 commit 4145ac8
Show file tree
Hide file tree
Showing 45 changed files with 244 additions and 144 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def resource_params
permitted = params.require(:session).permit(:access_token)
permitted[:provider] = :facebook
permitted[:request] = request
permitted[:user_model] = auth_user_model
permitted
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ class RegistrationsController < BaseController
private

def build_resource
@resource = ApiAuthentication.user_model.new(resource_params)
@resource = auth_user_model.new(resource_params)
end

def resource_params
params
.require(ApiAuthentication.user_model.name.downcase)
.permit(ApiAuthentication.configuration.registration_fields)
.require(auth_user_model.name.downcase)
.permit(ApiAuthentication.user_model_params(auth_user_model).fetch(:registration_fields))
end
end
end
7 changes: 5 additions & 2 deletions app/controllers/api_authentication/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,18 @@ def create
private

def build_resource
@resource = ::ApiAuthentication::UserAuthenticator.new(resource_params.merge(request: request))
@resource = ::ApiAuthentication::UserAuthenticator.new(resource_params)
end

def resource
@resource ||= ApiAuthentication::RefreshAuthorizer.new(request.headers).auth
end

def resource_params
params.require(:session).permit(:email, :password)
permitted = params.require(:session).permit(:login, :password)
permitted[:request] = request
permitted[:user_model] = auth_user_model
permitted
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ module RequestAuthorizeable
extend ActiveSupport::Concern

included do
before_action :check_base_policy

attr_reader :current_user

protect_from_forgery with: :exception, unless: -> { request.format.json? }
Expand All @@ -18,12 +16,9 @@ def authenticate!
@current_user = RequestAuthorizer.new(request.headers).auth
end

def base_policy
true
end

def check_base_policy
head :forbidden unless base_policy
def auth_user_model
model = params[:user_model] || ApiAuthentication.configuration.auth_models.first[:model]
model.constantize
end
end
end
8 changes: 0 additions & 8 deletions app/errors/api_authentication/auth.rb

This file was deleted.

8 changes: 0 additions & 8 deletions app/errors/api_authentication/token.rb

This file was deleted.

14 changes: 10 additions & 4 deletions lib/api_authentication.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ module SocialProviders
autoload :Facebook, 'api_authentication/social_providers/facebook'
end

module Errors
autoload :Auth, 'api_authentication/errors/auth'
autoload :SocialLogin, 'api_authentication/errors/social_login'
autoload :Token, 'api_authentication/errors/token'
end

def self.configure(&block)
block.call configuration
end
Expand All @@ -31,8 +37,8 @@ def self.configuration
@configuration ||= Configuration.new
end

def self.user_model
configuration.app_user_model_class_name.constantize
def self.user_model_params(user_model)
configuration.auth_models.find { |auth_model| auth_model[:model] == user_model.name }
end

def self.refresh_token_model
Expand All @@ -43,7 +49,7 @@ def self.push_token_model
configuration.app_push_token_model_class_name.constantize
end

def self.user_field_defined?(field)
configuration.user_fields.include?(field)
def self.user_field_defined?(model, field)
user_model_params(model).fetch(:registration_fields).include?(field)
end
end
28 changes: 15 additions & 13 deletions lib/api_authentication/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@ class Configuration
#
config_accessor(:controller_to_inherit_from) { 'ActionController::Base' }

#
# => fields for registration
#
config_accessor(:registration_fields) { %i[email password first_name last_name username birthday] }

#
# => Enable Registrations endpoint
#
Expand All @@ -35,9 +30,21 @@ class Configuration
config_accessor(:facebook_login) { true }

#
# => allow to set up in-app class name of user model
#
config_accessor(:app_user_model_class_name) { 'User' }
# => allow to set up in-app class name of users models
#
config_accessor(:auth_models) do
[
{
model: 'User',
registration_fields: %i[email password first_name last_name username birthday],
login_field: :email,
push_tokens: true,
refresh_tokens: true,
social_login: true,
facebook_registration_fields: %i[email password first_name last_name username birthday]
}
]
end

#
# => allow to set up in-app class name of refresh token model
Expand All @@ -49,11 +56,6 @@ class Configuration
#
config_accessor(:app_push_token_model_class_name) { 'PushToken' }

#
# => User model fields
#
config_accessor(:user_fields) { %i[email password first_name last_name username birthday] }

#
# => secret_key
#
Expand Down
9 changes: 9 additions & 0 deletions lib/api_authentication/errors/auth.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

module ApiAuthentication
module Errors
class Auth
class InvalidCredentials < StandardError; end
end
end
end
10 changes: 10 additions & 0 deletions lib/api_authentication/errors/social_login.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

module ApiAuthentication
module Errors
class SocialLogin
class NotAllowed < StandardError; end
class FacebookError < StandardError; end
end
end
end
10 changes: 10 additions & 0 deletions lib/api_authentication/errors/token.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

module ApiAuthentication
module Errors
class Token
class Missing < StandardError; end
class Invalid < StandardError; end
end
end
end
2 changes: 1 addition & 1 deletion lib/api_authentication/header_auth_finder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def initialize(headers)
def authorization
return headers['Authorization'].split(' ').last if headers['Authorization'].present?

raise ApiAuthentication::Token::Missing, I18n.t('api_authentication.errors.token.missing')
raise ApiAuthentication::Errors::Token::Missing, I18n.t('api_authentication.errors.token.missing')
end

private
Expand Down
2 changes: 1 addition & 1 deletion lib/api_authentication/json_web_token.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def self.decode(token)
body = JWT.decode(token, HMAC_SECRET)[0]
HashWithIndifferentAccess.new(body)
rescue JWT::DecodeError => e
raise ApiAuthentication::Token::Invalid, e.message
raise ApiAuthentication::Errors::Token::Invalid, e.message
end
end
end
7 changes: 6 additions & 1 deletion lib/api_authentication/models/push_token.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ module PushToken
extend ActiveSupport::Concern

included do
belongs_to ApiAuthentication.configuration.app_user_model_class_name.downcase.to_sym
belongs_to :user,
if ApiAuthentication.configuration.auth_models.count > 1
{ polymorphic: true }
else
{ class_name: ApiAuthentication.configuration.auth_models.first[:model] }
end

validates :token, presence: true

Expand Down
7 changes: 6 additions & 1 deletion lib/api_authentication/models/refresh_token.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ module RefreshToken
extend ActiveSupport::Concern

included do
belongs_to ApiAuthentication.configuration.app_user_model_class_name.downcase.to_sym
belongs_to :user,
if ApiAuthentication.configuration.auth_models.count > 1
{ polymorphic: true }
else
{ class_name: ApiAuthentication.configuration.auth_models.first[:model] }
end

validates :expired_at, :ip_address, :user_agent, presence: true
validates :token, uniqueness: true
Expand Down
20 changes: 11 additions & 9 deletions lib/api_authentication/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,21 @@ module User
included do
has_secure_password

if ApiAuthentication.configuration.app_refresh_token_model_class_name
has_many ApiAuthentication.configuration.app_refresh_token_model_class_name.pluralize.underscore.to_sym
if ApiAuthentication.user_model_params(self).fetch(:refresh_tokens)
has_many ApiAuthentication.configuration.app_refresh_token_model_class_name.pluralize.underscore.to_sym,
as: ApiAuthentication.configuration.auth_models.count > 1 ? :user : nil
end

if ApiAuthentication.configuration.app_push_token_model_class_name
has_many ApiAuthentication.configuration.app_push_token_model_class_name.pluralize.underscore.to_sym
if ApiAuthentication.user_model_params(self).fetch(:push_tokens)
has_many ApiAuthentication.configuration.app_push_token_model_class_name.pluralize.underscore.to_sym,
as: ApiAuthentication.configuration.auth_models.count > 1 ? :user : nil
end

validates :email, format: { with: EMAIL_REGEX } if ApiAuthentication.user_field_defined?(:email)
validates :first_name, presence: true if ApiAuthentication.user_field_defined?(:first_name)
validates :last_name, presence: true if ApiAuthentication.user_field_defined?(:last_name)
validates :username, presence: true if ApiAuthentication.user_field_defined?(:username)
validates :birthday, presence: true if ApiAuthentication.user_field_defined?(:birthday)
validates :email, format: { with: EMAIL_REGEX } if ApiAuthentication.user_field_defined?(self, :email)
validates :first_name, presence: true if ApiAuthentication.user_field_defined?(self, :first_name)
validates :last_name, presence: true if ApiAuthentication.user_field_defined?(self, :last_name)
validates :username, presence: true if ApiAuthentication.user_field_defined?(self, :username)
validates :birthday, presence: true if ApiAuthentication.user_field_defined?(self, :birthday)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/api_authentication/refresh_authorizer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def auth
@token ||= ApiAuthentication.refresh_token_model.find_by(token: header_auth_finder.authorization)
return @token if @token.present? && !(@token.expired? || @token.revoked?)

raise ApiAuthentication::Token::Invalid, I18n.t('api_authentication.errors.token.invalid')
raise ApiAuthentication::Errors::Token::Invalid, I18n.t('api_authentication.errors.token.invalid')
end

private
Expand Down
3 changes: 1 addition & 2 deletions lib/api_authentication/refresh_token_creator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ def initialize(params)
end

def create
ApiAuthentication.refresh_token_model.create!(
user_id: user.id,
user.refresh_tokens.create!(
token: ApiAuthentication.refresh_token_model.generate_token,
expired_at: ApiAuthentication.configuration.refresh_token_exp.from_now,
ip_address: request.remote_ip,
Expand Down
10 changes: 7 additions & 3 deletions lib/api_authentication/request_authorizer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

module ApiAuthentication
class RequestAuthorizer
def initialize(headers = {})
def initialize(headers)
@header_auth_finder = HeaderAuthFinder.new(headers)
end

def auth
@user ||= ApiAuthentication.user_model.find_by!(id: decoded_auth_token[:user_id])
@user ||= user_model.find_by!(id: decoded_auth_token.fetch(:user_id))
rescue ActiveRecord::RecordNotFound => _e
raise ApiAuthentication::Token::Invalid, I18n.t('api_authentication.errors.token.invalid')
raise ApiAuthentication::Errors::Token::Invalid, I18n.t('api_authentication.errors.token.invalid')
end

private
Expand All @@ -19,5 +19,9 @@ def auth
def decoded_auth_token
@decoded_auth_token ||= JsonWebToken.decode(header_auth_finder.authorization)
end

def user_model
@user_model ||= decoded_auth_token.fetch(:user_model).constantize
end
end
end
24 changes: 18 additions & 6 deletions lib/api_authentication/social_login.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@

module ApiAuthentication
class SocialLogin
attr_reader :provider, :access_token, :user, :tokens, :request
attr_reader :provider, :access_token, :user, :tokens, :request, :user_model

def initialize(params)
@provider = params[:provider]
@access_token = params[:access_token]
@request = params[:request]
@user_model = params[:user_model]
end

def call
raise ApiAuthentication::Errors::SocialLogin::NotAllowed unless social_login_allowed?

save_info!
create_tokens!
end
Expand All @@ -27,16 +30,17 @@ def create_tokens!
end

def provider_data
@provider_data ||= "#{SocialProviders.name}::#{provider.capitalize}".constantize.new(access_token).fetch_data
@provider_data ||= "#{SocialProviders.name}::#{provider.capitalize}"
.constantize.new(access_token, registration_fields).fetch_data
end

def existing_user
@existing_user ||= ApiAuthentication.user_model.find_by("#{provider}_id": provider_data[:id])
@existing_user ||= ApiAuthentication.user_model.find_by(email: provider_data[:email]) if provider_data[:email]
@existing_user ||= user_model.find_by("#{provider}_id": provider_data[:id])
@existing_user ||= user_model.find_by(email: provider_data[:email]) if provider_data[:email]
end

def new_user
ApiAuthentication.user_model.new("#{provider}_id": provider_data[:id], password: SecureRandom.uuid)
user_model.new("#{provider}_id": provider_data[:id], password: SecureRandom.uuid)
end

def update_existing_user
Expand All @@ -52,7 +56,15 @@ def create_new_user
end

def registration_fields
ApiAuthentication.configuration.registration_fields.reject { |f| f == :password }
user_model_params.fetch(:"#{provider}_registration_fields").reject { |f| f == :password }
end

def user_model_params
@user_model_params ||= ApiAuthentication.user_model_params(user_model)
end

def social_login_allowed?
user_model_params.fetch(:social_login)
end
end
end
Loading

0 comments on commit 4145ac8

Please sign in to comment.