OK i'm not really a ROR person but I'm trying to modify an existing app (forked) from https://github.com/adamcooke/staytus
there's an /admin page which right now brings you to a haml login form with a username/password box.
What I'm attempting to do us change this functionally so that when /admin is hit devise and omniAuth will redirect to my IDP via OpenIDConnect ask the user to login with their creds, do the auth stuff and pending they get through then show the admin section...
here's what I've done thus far:
installed these gems
gem 'devise'
gem 'omniauth-rails_csrf_protection'
gem 'omniauth-azure-activedirectory-v2'
ran the devise config/install:
added config.rb
def omniauth_oidc?
result = ENV['OMNIAUTH_OIDC'] == '1'
puts "omniauth_oidc? result: #{result}"
I've tried all combinations of routes:
devise_scope :user do
devise_for :users, controllers: { omniauth_callbacks: 'admin/omniauth_callbacks' }
end
devise_scope :user do
get '/admin/omniauth_callbacks' => 'admin/omniauth_callbacks#azure_activedirectory_v2'
end
namespace :admin do
get '/omniauth_callbacks', to: 'omniauth_callbacks#azure_activedirectory_v2'
end
result
end
I've also tried " to: 'sessions#create'" routes but clearly I'm missing something here...
added OmniauthCallbacksControler
class Admin::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def azure_activedirectory_v2
puts "request.env['omniauth.auth']: #{request.env['omniauth.auth'].inspect}"
response_params = request.env['omniauth.auth']
if response_params.nil?
Rails.logger.error("request.env['omniauth.auth'] is nil")
raise "request.env['omniauth.auth'] is nil"
else
response_params = response_params['info']
end
#user = User.find_by!(email: response_params['email'])
if #user&.persisted?
sign_in_and_redirect #user, event: :authentication, location: admin_root_path
else
flash[:danger] = 'You have not yet an account!'
redirect_back(fallback_location: admin_root_path)
end
end
end
added omniauth.rb initializer
Rails.application.config.middleware.use OmniAuth::Builder do
provider :developer if Rails.env.development?
provider :azure_activedirectory_v2,
{
client_id: ENV[''],
client_secret: ENV[''],
tenant_id: ENV[']
}
end
given all of the above I still haven't gotten /admin to redirect to my IDP login ? WHY ?
Related
So I'm trying to configure Rails with Google oauth via devise, i have followed official docs described here. After everything i get this error when clicking the google signup button
Access to fetch at 'url' (redirected from 'http://localhost:3000/users/auth/google_oauth2') from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Googling around i found you need to enable CORS, naturally i did just that
in my application.rb in added this
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins '*'
resource '*', headers: :any, methods: [:get, :post, :patch, :put]
end
end
This is my callbacks controller
class Users::CallbacksController < Devise::OmniauthCallbacksController
skip_before_action :verify_authenticity_token
def google_oauth2
admin = User.from_omniauth(from_google_params)
redirect_to root_path
end
private
def from_google_params
#from_google_params ||= {
uid: auth.uid,
email: auth.info.email,
full_name: auth.info.name,
avatar_url: auth.info.image,
is_member: false
}
end
def auth
#auth ||= request.env['omniauth.auth']
end
end
EDIT
So after alot of trial n errors i have it working, kinda. So when i click the signup with google it still throws an error,
But, when clicking the link in the google console it successfully goes there, any ideas ?
Try this setup with devise at master branch and gem omniauth-rails_csrf_protection
devise.rb
config.omniauth :google_oauth2, "API_KEY", "API_SECRET"
routes.rb
devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
user.rb
devise :omniauthable, omniauth_providers: [:google_oauth2]
app/controllers/users/omniauth_callbacks_controller.rb:
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def google_oauth2
#user = User.from_omniauth(request.env['omniauth.auth'])
if #user.persisted?
flash[:notice] = I18n.t 'devise.omniauth_callbacks.success', kind: 'Google'
sign_in_and_redirect #user, event: :authentication
else
session['devise.google_data'] = request.env['omniauth.auth'].except('extra') # Removing extra as it can overflow some session stores
redirect_to new_user_registration_url, alert: #user.errors.full_messages.join("\n")
end
end
end
user.rb
def self.from_omniauth(access_token)
data = access_token.info
user = User.where(email: data['email']).first
unless user
user = User.create(
email: data['email'],
password: Devise.friendly_token[0,20])
end
user
end
gemfile:
gem "devise", github: "heartcombo/devise", branch: "master"
gem "omniauth-rails_csrf_protection"
gem "omniauth-google-oauth2"
view:
<%= link_to "Sign in with Google", user_omniauth_authorize_path(:google_oauth2), method: :post %>
I have a Rails 5 site which consists of 2 parts:
Admin area
API-only client area
I'm using Devise for both parts and https://github.com/lynndylanhurley/devise_token_auth gem for the API frontend.
The problem is about using the omniauth authentication. When I omniauth authenticate into the admin area - everything is ok - I get back some successful HTML-response.
But the problem is that I'm getting the same HTML-response in the API-area - but I need some JSON-response - not HTML one.
Here is my code:
config/routes.rb
Rails.application.routes.draw do
devise_for :users, controllers: { sessions: 'users/sessions', :omniauth_callbacks => 'users/omniauth_callbacks' }
namespace :api do
mount_devise_token_auth_for 'User', at: 'auth', controllers: { sessions: 'api/users/sessions', :omniauth_callbacks => 'api/users/omniauth_callbacks' }
end
end
app/models/user.rb
class User < ApplicationRecord
# Include default devise modules.
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable,
:omniauth_providers => [:facebook, :vkontakte]
include DeviseTokenAuth::Concerns::User
devise :omniauthable
def self.from_omniauth_vkontakte(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.email = auth.extra.raw_info.first_name.to_s + "." + auth.extra.raw_info.last_name.to_s + '#vk.com'
user.password = Devise.friendly_token[0,20]
end
end
end
app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def vkontakte
#user = User.from_omniauth_vkontakte(request.env["omniauth.auth"])
if #user.persisted?
sign_in_and_redirect #user, :event => :authentication #this will throw if #user is not activated
set_flash_message(:notice, :success, :kind => "Vkontakte") if is_navigational_format?
else
session["devise.vkontakte_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
end
config/initializers/devise.rb
Devise.setup do |config|
config.omniauth :facebook, ENV["FACEBOOK_APP_ID"], ENV["FACEBOOK_APP_SECRET"], provider_ignores_state: true
config.omniauth :vkontakte, ENV["VKONTAKTE_APP_ID"], ENV["VKONTAKTE_APP_SECRET"]
end
Gemfile
gem 'omniauth'
gem 'omniauth-facebook'
gem 'omniauth-vkontakte'
Gemfile.lock
devise (4.3.0)
devise_token_auth (0.1.42)
Here's my log:
Started GET "/api/auth/vkontakte" for 127.0.0.1 at 2017-06-20 17:34:23
+0300
Started GET "/omniauth/vkontakte?namespace_name=api&resource_class=User" for
127.0.0.1 at 2017-06-20 17:34:23 +0300
I, [2017-06-20T17:34:23.237270 #15747] INFO -- omniauth: (vkontakte) Request phase initiated.
Started GET "/omniauth/vkontakte/callback?code=0b8446c5fe6873bb12&state=52254649eb899e3b743779a1a4afc0304f249a6dd90b4415" for 127.0.0.1 at 2017-06-20 17:34:23 +0300
I, [2017-06-20T17:34:23.672200 #15747] INFO -- omniauth: (vkontakte) Callback phase initiated. Processing by Users::OmniauthCallbacksController#vkontakte as */* Parameters: {"code"=>"0b8446c5fe6873bb12", "state"=>"52254649eb899e3b743779a1a4afc0304f249a6dd90b4415"}
I guess that the problem is about a so-called "callback" url. I don't understand where it is set. It is obvious from the log that at the end of the auth process the GET "/omniauth/vkontakte/callback..." query is called. And probably it is called always - no matter if I initiated the oath sequence from admin or api client area.
I use Chrome Postman to make the API query http://localhost:3000/api/auth/vkontakte - and I get the HTML-response back ("successful login etc.") - but I need surely some JSON-response.
Is there a way to dynamically change the callback path depending on some precondition?
Is the callback query somewhat different depending on from where the oath procedure was initiated?
EDIT1:
This is not a single problem here unfortunately. Looks like the oauth is simply not implemented in the https://github.com/lynndylanhurley/devise_token_auth gem. So, even if I succeed to switch the oauth login procedure to the JSON way - how do I login the user the devise_token_auth-way - generating 3 tokens etc...? The app/controllers/users/omniauth_callbacks_controller.rb needs to be totally reimlemented.
You can render json from your OmniauthCallbacksController based on some extra parameter provided when your request a connection from the API for example.
These extra parameters will be availables in this hash request.env["omniauth.params"].
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def vkontakte
#user = User.from_omniauth_vkontakte(request.env["omniauth.auth"])
if #user.persisted?
sign_in #user, :event => :authentication #this will throw if #user is not activated
set_flash_message(:notice, :success, :kind => "Vkontakte") if is_navigational_format?
if request.env["omniauth.params"]["apiRequest"]
render status: 200, json: { message: "Login success" }
else
redirect_to after_sign_in_path_for(#user)
end
else
session["devise.vkontakte_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
end
You can this extra parameters by calling the auth helper with additional parameters, they will be passed to your OmniauthController : user_vkontakte_omniauth_authorize_path(api_request: true) (Or whatever your route helper is)
I ended up implementing my own oauth callback procedure - instead of using one from the devise_token_auth gem.
The devise_token_auth gem does contain the oauth authentication - but it appears to be not working properly.
Here are my code changes:
config/routes.rb
Rails.application.routes.draw do
devise_for :users, controllers: { sessions: 'users/sessions', :omniauth_callbacks => 'users/omniauth_callbacks' }
namespace :api do
mount_devise_token_auth_for 'User', at: 'auth', controllers: { sessions: 'api/users/sessions'}
end
end
app/controllers/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
include DeviseTokenAuth::Concerns::SetUserByToken
def vkontakte
#user = User.from_omniauth_vkontakte(request.env["omniauth.auth"])
namespace_name = request.env["omniauth.params"]["namespace_name"]
if #user.persisted?
if namespace_name && namespace_name == "api"
#client_id = SecureRandom.urlsafe_base64(nil, false)
#token = SecureRandom.urlsafe_base64(nil, false)
#user.tokens[#client_id] = {
token: BCrypt::Password.create(#token),
expiry: (Time.now + DeviseTokenAuth.token_lifespan).to_i
}
#user.save
#resource = #user # trade-off for "update_auth_header" defined in "DeviseTokenAuth::Concerns::SetUserByToken"
sign_in(:user, #user, store: false, bypass: false)
render json: #user
else
sign_in_and_redirect #user, :event => :authentication #this will throw if #user is not activated
set_flash_message(:notice, :success, :kind => "Vkontakte") if is_navigational_format?
end
else
session["devise.vkontakte_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
end
The inclusion of include DeviseTokenAuth::Concerns::SetUserByToken provides 5 auth headers in response:
access-token →BeX35KJfYVheKifFdwMPag
client →96a_7jXewCThas3mpe-NhA
expiry →1499340863
token-type →Bearer
uid →376449571
But the response still lacks these headers (available at a common sign-in):
Access-Control-Allow-Credentials →true
Access-Control-Allow-Methods →GET, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Origin →chrome-extension://aicmkgpgakddgnaphhhpliifpcfhicfo
Access-Control-Max-Age →1728000
I don't know whether they are important and if yes - how to provide them.
PS The same identical approach works with Facebook too.
I have been following Ryan Boland's Rails multitenancy tutorial, but have run into a snag with devise_invitable.
I create a new account and user/account owner on a chosen subdomain (mysubdomain.lvh.me:3000), from which I can send a user invitation just fine. I open the invitation link in an incognito Chrome session to ensure I am not logged in or have any current session. Upon clicking on the invitation link, I am redirected to the sign in page (mysubdomain.lvh.me:3000/users/sign_in) and see a flash notice: "The invitation token provided is not valid!"
Related to this one:
Rails 4 devise_invitable invitation token invalid
[SOLVED]
In case anyone has the same issue, override the invitations controller and change the Tenant with Apartment:
# app/controllers/users/invitations_controller.rb
class Users::InvitationsController < Devise::InvitationsController
private
def resource_from_invitation_token
Apartment::Tenant.switch!(request.subdomain) //ADD THIS BABY!
unless params[:invitation_token] &&
self.resource = resource_class.find_by_invitation_token(params[:invitation_token], true)
set_flash_message(:alert, :invitation_token_invalid)
redirect_to after_sign_out_path_for(resource_name)
end
end
end
Remember to update your routes also, like this:
# config/routes.rb
devise_for :users, :controllers => { :invitations => 'users/invitations' }
EDIT: Resolved this issue. Ended up being trivial; I forgot to restart the Rails server after editing my config file.
I'm trying to integrate Stripe OAuth in a Rails application w/ Devise using this tutorial. I think I've followed it to a t, but I'm receiving this error when I go to Connect to Stripe on my app.
{"error":{"message":"No application matches the supplied client
identifier"}}
I'm not sure how to check whether the client identifier I declare in my application.yml is even being passed in, or what value it's reading. Here's my setup so far:
config/application.yml (has my IDs from Stripe account - I edited them out here):
STRIPE_CONNECT_CLIENT_ID: "____________"
STRIPE_SECRET_KEY: "_____________"
config/initializers/devise.rb
Devise.setup do |config|
config.omniauth :stripe_connect,
ENV['STRIPE_CONNECT_CLIENT_ID'],
ENV['STRIPE_SECRET_KEY'],
:scope => 'read_write',
:stripe_landing => 'register'
*other config code*
end
config/routes.rb
Studiocracy::Application.routes.draw do
devise_for :users, :controllers => { :omniauth_callbacks => "omniauth_callbacks", registrations: 'registrations' }
*other stuff*
end
controllers/omniauth_callbacks_controller.rb
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def stripe_connect
#user = current_user
if #user.update_attributes({
provider: request.env["omniauth.auth"].provider,
uid: request.env["omniauth.auth"].uid,
access_code: request.env["omniauth.auth"].credentials.token,
publishable_key: request.env["omniauth.auth"].info.stripe_publishable_key
})
# anything else you need to do in response..
sign_in_and_redirect #user, :event => :authentication
set_flash_message(:notice, :success, :kind => "Stripe") if is_navigational_format?
else
session["devise.stripe_connect_data"] = request.env["omniauth.auth"]
redirect_to new_user_registration_url
end
end
end
app/views/devise/registrations/edit.html.erb
<%= link_to image_tag('stripe.png'), user_omniauth_authorize_path(:stripe_connect) %>
I did the same silly mistake. Later i found out, it is because of wrong client id i am passing. Just chech href tag of your connect to stripe button once and recheck.
I got a solution by adding just a foward slash (/) after the main URL before the question mark (?):
Before:
https://connect.stripe.com/oauth/v2/authorize?scope=read_write&client_id={client_id}&redirect_uri={https//:example.com}
v
https://connect.stripe.com/oauth/v2/authorize/?scope=read_write&client_id={client_id}&redirect_uri={https//:example.com}
^
I am trying to authenticate my new Shopify app. First, my authenticate method redirects the shop owner to Shopify's authentication page:
def authenticate
ShopifyAPI::Session.setup({:api_key => "123", :secret => "456"})
session = ShopifyAPI::Session.new("someshop.myshopify.com")
redirect_to session.create_permission_url(["read_orders"], "https://myapp.com/shopify/post_authenticate?user=someshop")
end
Once the shop owner has approved the integration, the redirect uri triggers my post_authenticate method:
def post_authenticate
ShopifyAPI::Session.setup({:api_key => "123", :secret => "456"})
session = ShopifyAPI::Session.new("#{params[:user]}.myshopify.com")
token = session.request_token(:code => params[:code], :signature => params[:signature], :timestamp => params[:timestamp])
end
But the request_token method returns the following error:
#<ShopifyAPI::ValidationException: Invalid Signature: Possible malicious login>
I have read somewhere that you need to be in the same ShopifyAPI session while doing all of this, but it does not say so in the documentation. And the example app takes a very different approach than the documentation.
As per my comment, I utilize the omniauth methodology for authenticating. Here's a gist of the code for reference. https://gist.github.com/agmcleod/7106363317ebd082d3df. Put all the snippets below.
class ApplicationController < ActionController::Base
protect_from_forgery
force_ssl
helper_method :current_shop, :shopify_session
protected
def current_shop
#current_shop ||= Shop.find(session[:shop_id]) if session[:shop_id].present?
end
def shopify_session
if current_shop.nil?
redirect_to new_login_url
else
begin
session = ShopifyAPI::Session.new(current_shop.url, current_shop.token)
ShopifyAPI::Base.activate_session(session)
yield
ensure
ShopifyAPI::Base.clear_session
end
end
end
end
In my login controller:
def create
omniauth = request.env['omniauth.auth']
if omniauth && omniauth[:provider] && omniauth[:provider] == "shopify"
shop = Shop.find_or_create_by_url(params[:shop].gsub(/https?\:\/\//, ""))
shop.update_attribute(:token, omniauth['credentials'].token)
shopify_session = ShopifyAPI::Session.new(shop.url, shop.token)
session[:shop_id] = shop.to_param
redirect_to root_url
else
flash[:error] = "Something went wrong"
redirect_to root_url
end
end
config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
provider :shopify, Settings.api_key, Settings.api_secret,
scope: 'write_products,write_script_tags,read_orders',
setup: lambda { |env| params = Rack::Utils.parse_query(env['QUERY_STRING'])
env['omniauth.strategy'].options[:client_options][:site] = "http://#{params['shop']}" }
end
Then in your routes file, map the create action of your session appropriately:
match '/auth/shopify/callback', :to => 'login#create'
From there i use the shopify_session method as an around filter on the appropriate controllers.