We just updated our ruby version to 2.0 from 1.9.3. From what I can tell we haven't updated omniauth omniauth-facebook or oauth in the upgrade. The upgrade however broke facebook login. Looking at the logs I don't see an email coming back in the omniauth hash.
Here's my initializer, which loads my yml file where I define the secret and whatnot.
omniauth.rb initializer
class OmniAuthConfig
class << self
def load(file = 'omniauth.yml')
configuration_file = File.join(Rails.root, 'config', file)
if File.exists?(configuration_file)
File.open(configuration_file) do |configuration|
configuration = YAML.load(configuration)[Rails.env.to_sym]
configuration.each do |key, value|
cattr_accessor(key)
send("#{key}=", value)
end
end
end
end
end
end
OmniAuthConfig.load
Rails.application.config.middleware.use OmniAuth::Builder do
provider :facebook,
OmniAuthConfig.facebook_api_key,
OmniAuthConfig.facebook_api_secret,
scope: 'email',
client_options: { ssl: { ca_file: "/etc/ssl/certs/ca-certificates.crt" } }
end
Censored logs:
--- !ruby/hash:OmniAuth::AuthHash
provider: facebook
uid:
info: !ruby/hash:OmniAuth::AuthHash::InfoHash
image: http://graph.facebook.com//picture?type=square
credentials: !ruby/hash:OmniAuth::AuthHash
token: HUGE_TOKEN_STRING
expires_at: 1485547306
expires: true
extra: !ruby/hash:OmniAuth::AuthHash
raw_info: !binary |-
HASH_THINGY
Related
I'm having an issue where my integration tests do not seem to find the log_in_as method from my test_helper.rb
I have been following Michael Hart's Rails tutorial, so I was hoping not to massively refactor my code to try and get this to work. I would like to continue on through the book without having to exclude the tests, since it is pretty test heavy afterall.
Error:
UsersLoginTest#test_login_with_remembering:
NoMethodError: undefined method `log_in_as' for #<UsersLoginTest:0x00000005b18460>
test/integration/users_login_test.rb:43:in `block in <class:UsersLoginTest>'
User_login_test.rb:
require 'test_helper.rb'
class UsersLoginTest < ActionDispatch::IntegrationTest
.
.
.
test "login with remembering" do
log_in_as(#user, remember_me: '1')
assert_not_empty cookies['remember_token']
end
test "login without remembering" do
# Log in to set the cookie.
log_in_as(#user, remember_me: '1')
# Log in again and verify that the cookie is deleted.
log_in_as(#user, remember_me: '0')
assert_empty cookies['remember_token']
end
end
test_helper.rb:
ENV['RAILS_ENV'] ||= 'test'
class ActiveSupport::TestCase
fixtures :all
# Returns true if a test user is logged in.
def is_logged_in?
!session[:user_id].nil?
end
# Log in as a particular user.
def log_in_as(user)
session[:user_id] = user.id
end
end
class ActionDispatch::IntegrationTest
# Log in as a particular user.
def log_in_as(user, password: 'password', remember_me: '1')
post login_path, params: { session: { email: user.email,
password: password,
remember_me: remember_me } }
end
end
I had this same issue. There are two problems I had to fix:
Make sure there is only one test_helper.rb file, and
test_helper.rb is in the right folder
Hope this helps!
I'm developing an API using Rails 5.1.3 and I'm using the gem devise_token_auth for authenticate. Everything was working fine until I needed to customize the JSON renderized after an error occurred, like the client sending an request with an invalid email.
So, to do this, I redefined my routes from
mount_devise_token_auth_for 'User', at: 'auth'
to
mount_devise_token_auth_for 'User', at: 'auth', controllers: {
registrations: 'devise/registrations'
}
and created a file app/controllers/devise/registrations_controller.rb as below:
class RegistrationController < DeviseAuthToken::RegistrationController
def render_create_error
render 'devise/registrations/create_error.json'
end
def render_create_success
super
end
end
Now all requests that depends of RegistrationController are getting this error:
ActionView::Template::Error:
undefined method `protect_against_forgery?' for #<#<Class:0x007f84cfab70d8>:0x007f84cec53e10>
What I should do to fix this error?
Thanks in advance!
This is what I did on my rails server (rails 6). I have created folders called overrides where I place all my custom controllers / views
routes.rb
mount_devise_token_auth_for 'User', at: 'auth', controllers: {
registrations: 'overrides/registrations'
}
app/controllers/overrides/registrations_controller.rb
module Overrides
class RegistrationsController < DeviseTokenAuth::RegistrationsController
def render_create_success
render partial: 'overrides/registrations/render_create_success.json.jbuilder'
end
end
end
app/views/overrides/registrations/_render_create_success.json.jbuilder
json.status 'success'
json.data do
json.extract! #resource, :field1, :field2, etc.
end
Let me preface this question by saying that I'm new to SAML and barely understand how it works.
The Setup
I'm using the devise_saml_authenticatable gem with a Rails 4 app to achieve SSO. The Rails app acts as the service provider (SP). To test my setup, I created a OneLogin developer account and set up a SAML Test Connector (IdP w/attr w/ sign response) using the following attributes:
Configuration Tab
Audience: mysubdomain.onelogin.com
Recipient: http://mysubdomain.myapp.local:3000/saml/auth
ACS (Consumer) URL Validator: ^http://mysubdomain.myapp.local:3000/saml/auth$
ACS (Consumer) URL: http://mysubdomain.myapp.local:3000/saml/auth
Single Logout URL: http://mysubdomain.myapp.local:3000/saml/idp_sign_out
SSO Tab
Issuer URL: https://app.onelogin.com/saml/metadata/589819
SAML 2.0 Endpoint (HTTP): https://mysubdomain.onelogin.com/trust/saml2/http-post/sso/589819
SLO Endpoint (HTTP): https://mysubdomain.onelogin.com/trust/saml2/http-redirect/slo/589819
SAML Signature Algorithm: SHA-1
SHA Fingerprint: 60:9D:18:56:B9:80:D4:25:63:C1:CC:57:6D:B9:06:7C:78:BB:2C:F1
X.509 Certificate:
-----BEGIN CERTIFICATE-----
MIIEFzCCAv+gAwIBAgIUQYRVa1MQpUh0gJaznmXSF/SPqnowDQYJKoZIhvcNAQEF
BQAwWDELMAkGA1UEBhMCVVMxETAPBgNVBAoMCEZpcm1QbGF5MRUwEwYDVQQLDAxP
bmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgOTI1MzEwHhcN
MTYwOTIxMTU0NzQwWhcNMjEwOTIyMTU0NzQwWjBYMQswCQYDVQQGEwJVUzERMA8G
A1UECgwIRmlybVBsYXkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwW
T25lTG9naW4gQWNjb3VudCA5MjUzMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBALGVgocBj0ciHM3uKlWIcofPhOtzfJw1XpAdNynAvPtbCl7WE5+sLBoQ
ZF+oZ7Dl+wRW6DHMJCl9DdKcOaQA6/gr5bwt78IzZ8hWMoKQEPih+E0km6rKLYA8
M52vxtJxGs8Iqx60QvPEePQFMOA+xg73OExfM7W5LnXwNz/Pxgsr3lBif5oCC76j
SaTCFroV+TSjfOaYMW/lZrsS79KRIzA9I5XwUBe3bC8bsfQmZXgddCrkQUNSGGaS
7/jtFUlQ94+lAL+l3yoAiNAE6+mt48qqmyLfkKibXvnZ8dwuO272wpY4fEM+vFRy
pYrTajqvhY3hYIq8dLw3ominE5VECl8CAwEAAaOB2DCB1TAMBgNVHRMBAf8EAjAA
MB0GA1UdDgQWBBSxiuvTPxwOhh2pupID+tuyKCeceTCBlQYDVR0jBIGNMIGKgBSx
iuvTPxwOhh2pupID+tuyKCeceaFcpFowWDELMAkGA1UEBhMCVVMxETAPBgNVBAoM
CEZpcm1QbGF5MRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxv
Z2luIEFjY291bnQgOTI1MzGCFEGEVWtTEKVIdICWs55l0hf0j6p6MA4GA1UdDwEB
/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAYBe+5d3zpLZ7fcf3l3rXYeIxcpN+
9D2YZCbxsrBhY2Am4YE9nN+RaJXeDqeRBNtpayCZVxfHnXexRo1n7wxwTmosiydi
9yE7SY2xZf+3feQreF25atnn4tzVhxYONaX1njZMIt/TNa7A9aeDfHSD+vwSuYYB
hGxKT6HOkEAEBiXCZ/FcVNiB0D8bRwQhiJ3BTzXDfqHrmq8QYdn3Ejlqo62vMl6W
XeMXUoyv6cUc64Ap6E+XtEQI1E8YB5R8GtTs3Y1Oa2dD6yWyCyVJ20+Hi7IWAqXC
EfqstqXB7FoQ2rAt39cepnu1SOarvEYDMwYIaVNF3hoyodBybJJsAwAnCQ==
-----END CERTIFICATE-----
In my devise.rb I have the following configuration:
config.saml_create_user = false
config.saml_update_user = true
config.saml_default_user_key = :email
config.saml_session_index_key = :session_index
config.saml_use_subject = true
config.idp_settings_adapter = IdPSettingsAdapter
config.idp_entity_id_reader = DeviseSamlAuthenticatable::DefaultIdpEntityIdReader
Here is my IdPSettingsAdapter:
class IdPSettingsAdapter
def self.settings(idp_entity_id)
company = Company.find_by(idp_entity_id: idp_entity_id)
if company.present?
{
assertion_consumer_service_url: company.assertion_consumer_service_url,
assertion_consumer_service_binding: company.assertion_consumer_service_binding,
name_identifier_format: company.name_identifier_format,
issuer: company.issuer,
idp_entity_id: company.idp_entity_id,
authn_context: company.authn_context,
idp_slo_target_url: company.idp_slo_target_url,
idp_sso_target_url: company.idp_sso_target_url,
idp_cert_fingerprint: company.idp_cert_fingerprint
}
else
{}
end
end
end
Note that my user model Contact belongs_to Company, and that the SSO settings are stored in the Company model.
Here are my saml routes:
devise_for :contacts, skip: :saml_authenticatable, controllers: {
registrations: "registrations",
sessions: "sessions",
passwords: "passwords",
confirmations: "confirmations"
}
devise_scope :contact do
get '/sign_in' => 'sessions#new'
get '/sign_out' => 'sessions#destroy'
# SSO Routes
get 'saml/sign_in' => 'saml_sessions#new', as: :new_user_sso_session
post 'saml/auth' => 'saml_sessions#create', as: :user_sso_session
get 'saml/sign_out' => 'saml_sessions#destroy', as: :destroy_user_sso_session
get 'saml/metadata' => 'saml_sessions#metadata', as: :metadata_user_sso_session
match 'saml/idp_sign_out' => 'saml_sessions#idp_sign_out', via: [:get, :post]
end
Lastly here is my SamlSessionsController:
require "ruby-saml"
class SamlSessionsController < SessionsController
include DeviseSamlAuthenticatable::SamlConfig
skip_before_filter :verify_authenticity_token, raise: false
before_action :authorize_viewer, except: [:metadata]
protect_from_forgery with: :null_session, except: :create
def new
idp_entity_id = Company.friendly.find(#_request.env['HTTP_HOST'].split('.')[0]).idp_entity_id
request = OneLogin::RubySaml::Authrequest.new
action = request.create(saml_config(idp_entity_id))
redirect_to action
end
def metadata
idp_entity_id = Company.friendly.find(#_request.env['HTTP_HOST'].split('.')[0]).idp_entity_id
meta = OneLogin::RubySaml::Metadata.new
render :xml => meta.generate(saml_config(idp_entity_id)), content_type: 'application/samlmetadata+xml'
end
def create
#idp_entity_id = Company.friendly.find(#_request.env['HTTP_HOST'].split('.')[0]).idp_entity_id
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], settings: saml_config(#idp_entity_id))
if !response.is_valid?
puts "SAML FAILED WITH ERROR: "
puts response.errors
end
super
end
def idp_sign_out
company = Company.friendly.find(request.subdomain.downcase)
idp_entity_id = Company.friendly.find(#_request.env['HTTP_HOST'].split('.')[0]).idp_entity_id
if params[:SAMLRequest] && Devise.saml_session_index_key
saml_config = saml_config(idp_entity_id)
logout_request = OneLogin::RubySaml::SloLogoutrequest.new(params[:SAMLRequest], settings: saml_config(idp_entity_id))
resource_class.reset_session_key_for(logout_request.name_id)
# binding.pry
sign_out current_contact if contact_signed_in?
redirect_to company.after_slo_url.present? ? company.after_slo_url : 'https://' + company.issuer
# redirect_to generate_idp_logout_response(saml_config(idp_entity_id), logout_request.id)
elsif params[:SAMLResponse]
#Currently Devise handles the session invalidation when the request is made.
#To support a true SP initiated logout response, the request ID would have to be tracked and session invalidated
#based on that.
if Devise.saml_sign_out_success_url
redirect_to Devise.saml_sign_out_success_url
else
redirect_to action: :new
end
else
head :invalid_request
end
end
protected
# Override devise to send user to IdP logout for SLO
def after_sign_out_path_for(_)
request = OneLogin::RubySaml::Logoutrequest.new
request.create(saml_config)
end
def generate_idp_logout_response(saml_config, logout_request_id)
OneLogin::RubySaml::SloLogoutresponse.new.create(saml_config, logout_request_id, nil)
end
end
The Problem
When I manually save map the settings from my OneLogin adapter to my Company model (see screenshot), I'm able to authenticate as a user of my app using OneLogin as the identity provider (IdP). However now I need to provide a client with the XML metadata representing the app's setup. When I go to /saml/metadata.xml, I get the following configuration, which according to my client, is incorrect. The client didn't offer any further details about what the problem is. They are using PingFederate, if that matters.
<?xml version='1.0' encoding='UTF-8'?>
<md:EntityDescriptor ID='_a3581975-b73d-4784-a106-bafd61e15f87' xmlns:md='urn:oasis:names:tc:SAML:2.0:metadata'>
<md:SPSSODescriptor AuthnRequestsSigned='false' WantAssertionsSigned='false' protocolSupportEnumeration='urn:oasis:names:tc:SAML:2.0:protocol'>
<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>
<md:AssertionConsumerService Binding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' Location='https://mysubdomain.myapp.local:3000/saml/auth' index='0' isDefault='true'/>
</md:SPSSODescriptor>
</md:EntityDescriptor>
My question is, what am I doing wrong here and how can I correct it? As I said, I barely understand how SAML works under the hood.
There is no EntityID defined on that metadata XML.
If you try to verify the XML on a validation tool you will get
Line: 2 | Column: 0 --> Element
'{urn:oasis:names:tc:SAML:2.0:metadata}EntityDescriptor': The
attribute 'entityID' is required but missing.
If you review ruby-saml code, the EntityID is added to the metadata XML if a settings.issuer is defined. Can you verify if that data is provided? Maybe company.issuer that I see at IdPSettingsAdapter class has an empty value.
This is written in omniauth.rb an initializer.
Rails.application.config.middleware.use OmniAuth::Builder do
provider :google_oauth2,ID,SECRET,
{
:approval_prompt => '',
:scope => 'http://gdata.youtube.com,https://www.googleapis.com/auth/userinfo.email,https://www.googleapis.com/auth/userinfo.profile'
}
The returned auth hash does not have IMAGE element in the "info", why?????????
Replaced original info with x in the below HASH
*********************
--- !ruby/hash:OmniAuth::AuthHash
provider: google_oauth2
uid: 'xxxxxxxxxxxxxxxxxxxx'
info: !ruby/hash:OmniAuth::AuthHash::InfoHash
name: xxxx xxx
email: xxxxxxxxxx
first_name: xxxxxx
last_name: xxxxxxx
credentials: !ruby/hash:Hashie::Mash
token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
expires_at: 1365434778
expires: true
extra: !ruby/hash:Hashie::Mash
raw_info: !ruby/hash:Hashie::Mash
id: 'xxxxxxxxxxxxxxxxxxxxxxxxxxx'
email: xxxxxxxxxx.xxx#gmail.com
verified_email: true
name: xxxx xxxx
given_name: xxx
family_name: xxx
link: https://plus.google.com/xxxxxxxxxxx
gender: male
locale: en
*********************
I want the profile image, what am I doing wrong?
Also tried,
{:scope => 'userinfo.email,userinfo.profile'}
Not working!!
Not sure what that scope is doing, but I've just solved getting google info for myself.
Is that information the actual params sent to rails, or is it the saved user hash?
If it's not the actual params you get on login, you can use:
# sessions_controller.rb
raise env["omniauth.auth"].to_yaml
That'll show you everything that google is sending you.
If it's the user and If you've followed Railscasts like I was doing, then check that you've created a field to save the pics and that you've set the field to save the info from the hash. I did this:
# sessions_controller.rb
def create
user = User.from_omniauth(env["omniauth.auth"])
user.image = env["omniauth.auth"]['info']['image']
user.save
session[:user_id] = user.id
redirect_to root_url, :notice => "Signed in!"
end
If that's not what you're needing, can you be more specific?
I'm developing a web app that will let users tweet posts and links, but I can't seem to get Twitter and Omniauth to play nicely together. I'm currently running on Rails 3.0.6 and Ruby 1.8.7, with the Twitter gem 1.4.1 and Omniauth gem 0.2.5
I can authenticate the users fine, but when it comes to sending a tweet, I'm just given the error:
POST https://api.twitter.com/1/statuses/update.json: 401: Incorrect signature
I followed this tutorial, and have placed my consumer key and consumer secret in a Twitter configure block in my Omniauth initializer, but not the oauth token or oauth secret because these will surely be used on a per-user basis.
omniauth.rb
Twitter.configure do |config|
config.consumer_key = "XXXXXXXXXXXXXXXXXXXXXX"
config.consumer_secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
end
user.rb
def twitter
unless #twitter_user
provider = self.authentications.find_by_provider('twitter')
#twitter_user = Twitter::Client.new(:oauth_token => provider.token, :oauth_token_secret => provider.secret) rescue nil
end
#twitter_user
end
I then form the request using:
current_user.twitter.update("Hello World!")
And that's what then gives me the 401 error.
Any ideas? Thanks!
Your user.rb code is using the wrong format. They've changed quite a lot. You need something like this now:
require 'twitter'
class TwitterToken < ConsumerToken
TWITTER_SETTINGS={:site=>"http://api.twitter.com", :request_endpoint => 'http://api.twitter.com',}
def self.consumer
#consumer||=OAuth::Consumer.new credentials[:key],credentials[:secret],TWITTER_SETTINGS
end
def client
Twitter.configure do |config|
config.consumer_key = TwitterToken.consumer.key
config.consumer_secret = TwitterToken.consumer.secret
config.oauth_token = token
config.oauth_token_secret = secret
end
#client ||= Twitter::Client.new
end
end
I was having similar problems with that version of OmniAuth, I moved back to version 0.2.0 and all the 401's stopped happening.