Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/omniauth-rails.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require 'omniauth/rails'
3 changes: 0 additions & 3 deletions lib/omniauth-rails/version.rb

This file was deleted.

5 changes: 5 additions & 0 deletions lib/omniauth/rails.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
require 'omniauth'
require 'rails'

require 'omniauth/rails/version'
require 'omniauth/rails/railtie'
18 changes: 18 additions & 0 deletions lib/omniauth/rails/railtie.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
require 'omniauth/rails/request_forgery_protection'

module OmniAuth
module Rails
class Railtie < ::Rails::Railtie
initializer 'logger' do
OmniAuth.config.logger = ::Rails.logger
end

initializer 'request_forgery_protection' do
OmniAuth.config.allowed_request_methods = [:post]
OmniAuth.config.before_request_phase do |env|
RequestForgeryProtection::Current.new(env).call
end
end
end
end
end
23 changes: 23 additions & 0 deletions lib/omniauth/rails/request_forgery_protection.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module OmniAuth
module Rails
module RequestForgeryProtection
if ::Rails::VERSION::MAJOR == 3
require "omniauth/rails/request_forgery_protection/rails_3_2"

Current = Rails32
elsif ::Rails::VERSION::MAJOR == 4 && ::Rails::VERSION::MINOR == 0
require "omniauth/rails/request_forgery_protection/rails_4_0"

Current = Rails40
elsif ::Rails::VERSION::MAJOR == 4 && ::Rails::VERSION::MINOR == 1
require "omniauth/rails/request_forgery_protection/rails_4_1"

Current = Rails41
else
require "omniauth/rails/request_forgery_protection/rails_4_2"

Current = Rails42
end
end
end
end
73 changes: 73 additions & 0 deletions lib/omniauth/rails/request_forgery_protection/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
module OmniAuth
module Rails
module RequestForgeryProtection
class Base
def initialize(env)
@env = env
end

def request
@_request ||= ActionDispatch::Request.new(@env)
end

def session
request.session
end

def reset_session
request.reset_session
end

def params
@_params ||= request.parameters
end

def call
verify_authenticity_token
end

def verify_authenticity_token
return if verified_request?

if OmniAuth.logger && log_warning_on_csrf_failure
OmniAuth.logger.warn "Can't verify CSRF token authenticity"
end

handle_unverified_request
end

private

def handle_unverified_request
# Implemented by subclass.
end

def verified_request?
!protect_against_forgery? || request.get? || request.head? ||
valid_authenticity_token?(session, form_authenticity_param) ||
valid_authenticity_token?(session, request.headers['X-CSRF-Token'])
end

def valid_authenticity_token?
# Implemented by subclass.
end

def protect_against_forgery?
::ApplicationController.allow_forgery_protection
end

def request_forgery_protection_token
::ApplicationController.request_forgery_protection_token
end

def log_warning_on_csrf_failure
true
end

def form_authenticity_param
params[request_forgery_protection_token]
end
end
end
end
end
28 changes: 28 additions & 0 deletions lib/omniauth/rails/request_forgery_protection/rails_3_2.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
require 'omniauth/rails/request_forgery_protection/base'

module OmniAuth
module Rails
module RequestForgeryProtection
# Based on ActionController::RequestForgeryProtection in Rails 3.2.21.
class Rails32 < Base

private

# This is the method that defines the application behavior when a request is found to be unverified.
# By default, \Rails resets the session when it finds an unverified request.
def handle_unverified_request
reset_session
end

def valid_authenticity_token?(session, param)
form_authenticity_token == param
end

# Sets the token value for the current session.
def form_authenticity_token
session[:_csrf_token] ||= SecureRandom.base64(32)
end
end
end
end
end
21 changes: 21 additions & 0 deletions lib/omniauth/rails/request_forgery_protection/rails_4_0.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require 'omniauth/rails/request_forgery_protection/rails_3_2'

module OmniAuth
module Rails
module RequestForgeryProtection
# Based on ActionController::RequestForgeryProtection in Rails 4.0.13.
class Rails40 < Rails32

private

def forgery_protection_strategy
::ApplicationController.forgery_protection_strategy
end

def handle_unverified_request
forgery_protection_strategy.new(self).handle_unverified_request
end
end
end
end
end
12 changes: 12 additions & 0 deletions lib/omniauth/rails/request_forgery_protection/rails_4_1.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
require 'omniauth/rails/request_forgery_protection/rails_4_0'

module OmniAuth
module Rails
module RequestForgeryProtection
# Based on ActionController::RequestForgeryProtection in Rails 4.1.10.
class Rails41 < Rails40

end
end
end
end
88 changes: 88 additions & 0 deletions lib/omniauth/rails/request_forgery_protection/rails_4_2.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
require 'omniauth/rails/request_forgery_protection/rails_4_1'

require 'action_controller'
require 'active_support/security_utils'

module OmniAuth
module Rails
module RequestForgeryProtection
# Based on ActionController::RequestForgeryProtection in Rails 4.2.1.
class Rails42 < Rails41

private

def log_warning_on_csrf_failure
::ApplicationController.log_warning_on_csrf_failure
end

AUTHENTICITY_TOKEN_LENGTH = ActionController::RequestForgeryProtection::AUTHENTICITY_TOKEN_LENGTH

# Sets the token value for the current session.
def form_authenticity_token
masked_authenticity_token(session)
end

# Creates a masked version of the authenticity token that varies
# on each request. The masking is used to mitigate SSL attacks
# like BREACH.
def masked_authenticity_token(session)
one_time_pad = SecureRandom.random_bytes(AUTHENTICITY_TOKEN_LENGTH)
encrypted_csrf_token = xor_byte_strings(one_time_pad, real_csrf_token(session))
masked_token = one_time_pad + encrypted_csrf_token
Base64.strict_encode64(masked_token)
end

# Checks the client's masked token to see if it matches the
# session token. Essentially the inverse of
# +masked_authenticity_token+.
def valid_authenticity_token?(session, encoded_masked_token)
if encoded_masked_token.nil? || encoded_masked_token.empty? || !encoded_masked_token.is_a?(String)
return false
end

begin
masked_token = Base64.strict_decode64(encoded_masked_token)
rescue ArgumentError # encoded_masked_token is invalid Base64
return false
end

# See if it's actually a masked token or not. In order to
# deploy this code, we should be able to handle any unmasked
# tokens that we've issued without error.

if masked_token.length == AUTHENTICITY_TOKEN_LENGTH
# This is actually an unmasked token. This is expected if
# you have just upgraded to masked tokens, but should stop
# happening shortly after installing this gem
compare_with_real_token masked_token, session

elsif masked_token.length == AUTHENTICITY_TOKEN_LENGTH * 2
# Split the token into the one-time pad and the encrypted
# value and decrypt it
one_time_pad = masked_token[0...AUTHENTICITY_TOKEN_LENGTH]
encrypted_csrf_token = masked_token[AUTHENTICITY_TOKEN_LENGTH..-1]
csrf_token = xor_byte_strings(one_time_pad, encrypted_csrf_token)

compare_with_real_token csrf_token, session

else
false # Token is malformed
end
end

def compare_with_real_token(token, session)
ActiveSupport::SecurityUtils.secure_compare(token, real_csrf_token(session))
end

def real_csrf_token(session)
session[:_csrf_token] ||= SecureRandom.base64(AUTHENTICITY_TOKEN_LENGTH)
Base64.strict_decode64(session[:_csrf_token])
end

def xor_byte_strings(s1, s2)
s1.bytes.zip(s2.bytes).map { |(c1,c2)| c1 ^ c2 }.pack('c*')
end
end
end
end
end
5 changes: 5 additions & 0 deletions lib/omniauth/rails/version.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module OmniAuth
module Rails
VERSION = '0.0.1'
end
end
8 changes: 4 additions & 4 deletions omniauth-rails.gemspec
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# coding: utf-8
lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "omniauth-rails/version"
require "omniauth/rails/version"

Gem::Specification.new do |spec|
spec.name = "omniauth-rails"
spec.version = OmniAuthRails::VERSION
spec.version = OmniAuth::Rails::VERSION
spec.authors = ["Erik Michaels-Ober", "Douwe Maan"]
spec.email = ["sferik@gmail.com", "douwe@gitlab.com"]

Expand All @@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
spec.files = `git ls-files -z`.split("\x0")
spec.require_paths = ["lib"]

spec.add_dependency "omniauth"
spec.add_dependency "rails"
spec.add_dependency "omniauth", "~> 1.2.2"
spec.add_dependency "rails", ">= 3.2, < 5"
spec.add_development_dependency "bundler", "~> 1.9"
end