I am designing a shopify app which enables customers buying product through mobile phones. The scenario is customers need to be able to sign up with omniauth and then they can get the products info from the app. However, before any customer can get products info from a shop, my shopify app should first authenticate with the shop owner using omniauth.
The problem now is devise will modify omniauth default authenticate strategy. If I use shopify_api, I auth through path auth/shopify, it can work without devise installed. If devise is installed, it will redirect auth/shopify to omniauth/shopify. I find this path is generated by devise. How can I skip devise and use the original omniauth path? Thanks.
Devsie has a good tutorial on how to separate omniauthable out of your devise model. This will allow you to configure your own omniauth settings.
Once you've set-up a provider for both providers in config/initializers/omniauth.rb and removed devise.omniauth from conifg/initializers/devise.rb, you'll need to set-up your routes to handle the response from OAuth differently.
routes.rb
devise_scope :user do
get "/auth/:action/callback", to: 'users/omniauth_callbacks', constraints: { action: /facebook/ }
end
get 'auth/:action/callback' => 'another_controller', constraints: { action: /shopify/ } # connections
Then in another_controller.rb
class AnotherController
def shopify
auth_hash = request.env['omniauth.auth']
shop = auth_hash[:uid]
token = auth_hash[:credentials][:token]
ShopifyAPI::Session.temp("#{shop}.myshopify.com", token) do
current_shop = ShopifyAPI::Shop.current
...
end
end
end
Hope this helps.
Related
I have a main app, this app has Users and Admins (different model and table)using devise.
The main app use doorkeeper to allow users to authenticate on another service. The client app use Omniauth to consume OAuth2.
I am now reaching a point where the client app need a backend and using the admin users from the main app make sense.
Here is my current strategy :
module OmniAuth
module Strategies
class ResaNetwork < OmniAuth::Strategies::OAuth2
option :name, :resa_network
option :client_options, {
#site: "http://dylog.ubuntu-sylario.net:3000",
site: ENV["CLIENT_OPTION_SITE"],
authorize_path: "/oauth/authorize"
}
uid do
raw_info["resource_owner_id"]
end
def raw_info
#raw_info ||= access_token.get('/api/me').parsed
end
end
end
end
For doorkeeper I will will use warden scope (I know it has nothing to do with oauth scope) user or admin depending on the oauth2 Scope in resource_owner_authenticator.
Is it possible to have a specific scope entered has a param with the signin path of omniauth? How?
Is my use of scope correct or is it a really ugly hack? For info, admin will access users data while users will only see their data in the API.
I have an existing rails app that is using devise as it's user authentication. I added a discourse forum and everything went smoothly and it resides on a subdomain. I have read the post at https://meta.discourse.org/t/official-single-sign-on-for-discourse/13045 but still don't know what to do with the devise side of things once the user logs in on the existing rails site. Currently this is the process as I understand it:
Step1: User hits Discourse forum on subdomain. User needs to login so clicks login button.
Step2: User is sent to the login page on the existing rails site.
Step3: User logs in on rails site.
Step4: User should be redirected to discourse forum subdomain logged in.
My question is - What do I need to to do to make it so that when a user logs in on step 3 they get redirected back to the subdomain? Has anyone successfully implemented this? I saw this code snippet on that walkthrough page:
class DiscourseSsoController < ApplicationController
def sso
secret = "MY_SECRET_STRING"
sso = SingleSignOn.parse(request.query_string, secret)
sso.email = "user#email.com"
sso.name = "Bill Hicks"
sso.username = "bill#hicks.com"
sso.external_id = "123" # unique to your application
sso.sso_secret = secret
redirect_to sso.to_url("http://l.discourse/session/sso_login")
end
end
Is this what I would need to add in my existing rails app? I'm guessing the parse checks if that information is in the url and if so it redirects once it finishes the devise login process, and if not it just functions as usual. Would I place this code somewhere in the devise files?
This is pretty straightforward. Following on from the instructions at https://meta.discourse.org/t/official-single-sign-on-for-discourse/13045 and extrapolating a little, I have this working:
1) Put the reference implementation - https://github.com/discourse/discourse/blob/master/lib/single_sign_on.rb - in your #{Rails.root}/lib directory
2) Add this route to routes.rb
get 'discourse/sso' => 'discourse_sso#sso'
3) Put this controller in your app/controllers directory
require 'single_sign_on'
class DiscourseSsoController < ApplicationController
before_action :authenticate_user! # ensures user must login
def sso
secret = "MY_SECRET_STRING"
sso = SingleSignOn.parse(request.query_string, secret)
sso.email = current_user.email # from devise
sso.name = current_user.full_name # this is a custom method on the User class
sso.username = current_user.email # from devise
sso.external_id = current_user.id # from devise
sso.sso_secret = secret
redirect_to sso.to_url("http://your_discource_server/session/sso_login")
end
end
4) Set up the SSO config in discourse to have the following
sso url: http://your_rails_server/discourse/sso
sso secret : what you set as MY_SECRET_STRING above
5) Disable other login types in discourse.
6) Try to login in discourse. It should work...
thank #DanSingerman
They updated since you posted the answer.
The controller now
class DiscourseSsoController < ApplicationController
def sso
secret = "MY_SECRET_STRING"
sso = DiscourseApi::SingleSignOn.parse(request.query_string, secret)
sso.email = "user#email.com"
sso.name = "Bill Hicks"
sso.username = "bill#hicks.com"
sso.external_id = "123" # unique id for each user of your application
sso.sso_secret = secret
redirect_to sso.to_url("http://l.discourse/session/sso_login")
end
end
Install the Discourse API gem or copy the SingleSignOn class in your lib folder.
This class is in the gem repo: https://github.com/discourse/discourse_api/blob/main/lib/discourse_api/single_sign_on.rb
About the query_string: if you are testing locally you should simulate the request coming from Discourse as it will contain a query with 2 parameters, the payload and the sig:
https://somesite.com/sso?sso=PAYLOAD&sig=SIG
My end goal is for users to have multiple 3rd party authentications at the same time.
Right now, I am using Devise to create users. Users can sign up via email or facebook or google and it works. But now, after they have already signed up, I need them to also verify with, say, youtube or soundcloud. So the user was created with devise, but I also need them to verify with other things.
Since Devise hogs omniauth for it's own purposes, I can't use omniauth on the side.
As I see it I have three options:
Try to monkeypatch devise and get it to allow multiple authentications at the same time on one user
Do oauth by hand on the side adjacent to current Devise implementation
Scrap Devise and do something different
I would greatly appreciate any advice or other options
I think this may be what you need: http://blog.joshsoftware.com/2010/12/16/multiple-applications-with-devise-omniauth-and-single-sign-on/
They open sourced their code too!
Provider: https://github.com/joshsoftware/sso-devise-omniauth-provider
Client: https://github.com/joshsoftware/sso-devise-omniauth-client
Or even better, check out this: http://communityguides.heroku.com/articles/16
Try to monkeypatch devise and get it to allow multiple authentications at the same time on one use
You don't need to monkeypatch devise --- you can have your own oauth controller the has
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
# handle if already a twitter user
# handle if a new user
# use the `sign_in user` to sign_in the user
end
def twitter
# handle if already a facebook user
# handle if a new user
end
end
and use it in routes
devise_for :user,
:controllers => {
:omniauth_callbacks => "users/omniauth_callbacks"
}
I'm using omniauth without devise for authentication, as I like it's simplicity. In addition to omniauth-facebook I use omniauth-identity to offer email/pw-authentication.
The railscast on omniauth-identity describes how to setup a customized registration and login page. But the default routes supplied by identity (/auth/identity and /auth/identity/register) are still accessible.
I would like to have these under my control, as I want only want to let invited users register. Is there any way to override those routes supplied by a rack middleware?
Trying to just
match "/auth/identity", to: "somewhere#else"
doesn't do the trick!
Is there maybe a configuration to turn these default routes off? The documentation isn't giving any details on this...
Unfortunately I'm fairly new to Rack, so I don't have enough insight yet, to solve this issue on my own!
I'd be glad, if someone could point me in the right direction!
An OmniAuth strategy object has a method request_phase which generates a html form and shows it to user. For "omniauth-identity" strategy this would be the form you see at /auth/identity url.
You can override the request_phase method and replace the form generator with, for example, a redirect to your custom login page (assuming you have it available at /login url). Place the following along with your omniauth initialization code:
module OmniAuth
module Strategies
class Identity
def request_phase
redirect '/login'
end
end
end
end
# Your OmniAuth::Builder configuration goes here...
In addition to 1gors and iains answer:
"/auth/identity/register" is served with GET as well, to override, I had to:
class OmniAuth::Strategies::Identity
alias :original_other_phase :other_phase
def other_phase
if on_registration_path? && request.get?
redirect '/sign_up'
else
original_other_phase
end
end
end
You can set method in omniauth.rb
:on_login => SessionsController.action(:new)
for example:
Rails.application.config.middleware.use OmniAuth::Builder do
provider :identity,
:fields => [:nickname],
:on_login => SessionsController.action(:new),
:on_registration => UsersController.action(:new),
:on_failed_registration => SessionsController.action(:registration_failure)
end
I have a rails app hosted on Heroku that am restricting access to by using a proxy service. The external server acts as intermediary for all requests and handles user authentication. Once a user has authenticated, the server (I think LDAP) adds the user name to the request header and redirects them to my app.
I would like to use the username from the request header to authenticate users in my app. Basically if the user doesn't exist I would create a user with that username (no password required) and if not I would just log them in. I will be storing the users in my app's database.
How should I do this? Is it possible to use Devise for this purpose?
Edit: I got it working with Devise/custom Warden strategy like this:
# config/initializers/my_strategy.rb
Warden::Strategies.add(:my_strategy) do
def valid?
true
end
def authenticate!
if !request.headers["my_key"]
fail!("You are not authorized to view this site.")
redirect!("proxy_url")
else
username = request.headers["my_key"]
user = User.find_by_username(username)
if user.nil?
user = User.create(:username => username)
end
success!(user)
end
end
end
#config/initializers/devise.rb
config.warden do |manager|
manager.default_strategies(:scope => :user).unshift :my_strategy
end
I need to make this as bullet proof as possible. Are there other security measures can I take to make sure someone can't spoof the request header and access my site?
I think using devise can be a little more overkill, but you can. You just need define a warden strategie. in devise or use only warden in this purpose.