Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Omniauth without external window #654

Open
killerham opened this issue Jun 3, 2016 · 24 comments
Open

Omniauth without external window #654

killerham opened this issue Jun 3, 2016 · 24 comments
Labels

Comments

@killerham
Copy link

My users want to login with facebook and I have it setup where they can log into facebook using Facbook SDK on iOS. I want to pass the oauth access_token from SDK to omniauth, skipping the external window that devise_token_auth handles.

My question is how can I send an API request to /auth/:provider to go through the omniauth process when I already have an omniauth token?

I also am using the facebook-access-token gem which will handle the oauth token.

Related issues:
#556
#323
#175

Any advice is appreciated!

@killerham
Copy link
Author

Anyone have any tips on this?

@killerham
Copy link
Author

Any solutions are appreciated!

@glapworth
Copy link

I couldn't find a solution to this using devise_token_auth, and eventually gave up and used devise along with the tiddle gem.

I've uploaded an example rails app, along with an android app currently using Google oAuth2 for authentication. I wonder if that might help you?

https://github.com/glapworth/boilerplate-android-rails-api-oauth

@killerham
Copy link
Author

@glapworth I didn't want to re-implement a bunch of my tests and different API setups. However I did find a solution!

Send a GET request to omniauth/:provider/callback with "access_token": token, "resource_class": "User"} as params.

Override devise's OmniAuthCallbacksController not token_auth's.

def self.provides_callback_for(provider)
class_eval %Q{
      def #{provider}

        if request.format == 'json'
          # derive target redirect route from 'resource_class' param, which was set
          # before authentication.
          redirect_route = "/api/v2/auth/#{provider}/callback?\#{params.to_param}"

          # preserve omniauth info for success route. ignore 'extra' in twitter
          # auth response to avoid CookieOverflow.
          session['dta.omniauth.auth'] = request.env['omniauth.auth'].except('extra')
          session['dta.omniauth.params'] = request.env['omniauth.params']

          redirect_to redirect_route

          return
        end
        # else do standard devise stuff
    end
    }
  end

[:twitter, :facebook, :facebook_access_token, :linked_in].each do |provider|
    provides_callback_for provider
  end

As you can see... this method is super hacky... I hardcoded the redirect route. If anyone has a better suggestion for this that would be great! Most of the code was taken from DeviseTokenAuth::OmniauthCallbacksController #redirect_callbacks

@philippe3973
Copy link

+1, waiting for any more answers.

@mohamedelfiky
Copy link

+1

1 similar comment
@augustosamame
Copy link

+1

@tobeee
Copy link

tobeee commented Nov 17, 2016

Did anyone find a better solution for this?

@aaronshim
Copy link

+1

@Charlie-Hua
Copy link
Contributor

I just created 2 pull request for this purpose, FYI!
lynndylanhurley/ng-token-auth#341
#793

@mmahalwy
Copy link

+1 on this.

@gp3gp3gp3
Copy link

I'm confused. It seems as though one of the main attractions for using token authentication is being able to authenticate a user on a phone using something like facebook-sdk on the client. Is there some standard way to authenticate a mobile user on my rails server that I'm not seeing? I was under the impression that devise is more or less a standard for this, but it feels like I'm jumping through a lot of hoops here( overriding devise controller methods ) just to make this gem not use a redirect for omniauth, and ultimately being able to play nice with the iOS/Android facebook-sdk. If this isn't the standard what is? Or do most people just roll their own authentication entirely?

@geoandri
Copy link

geoandri commented Apr 6, 2017

Any news on this?

@vexsnare
Copy link

vexsnare commented Sep 13, 2017

+1
Any update on this? Or is there any other alternative for those who are using rails as their backend API and using FACEBOOK login in their mobile APP.

I am stuck at this, I am working on something similar to Tinder facebook login where I need to create account in backend once I get fb_token.

@hugohow
Copy link

hugohow commented Sep 15, 2017

+1

@geoandri
Copy link

geoandri commented Sep 26, 2017

I have implemented a basic working solution on this using the Koala Gem. I created a route for this purpose eg 'customer_facebook_auth'. Then in the relevant controller using the facebook token that the mobile app acquired I create the new user (customer in my case) with the facebook user attributes. The facebook user attributes can be fetched using the Koala library gem.

class Api::V1::CustomerFacebookAuthController < ApplicationController
  def login_customer
    @graph = Koala::Facebook::API.new(params['access_token'])
    @f_profile = @graph.get_object('me', fields: ['email', 'id', 'name'])

    create_and_sign_in_user @f_profile
  end


  private

  def create_and_sign_in_user facebook_profile
    if Customer.where(provider: 'email', email: facebook_profile['email']).count > 0
      render json: 'This email has already been taken'
    else
      @resource = Customer.where(
        uid: facebook_profile['id'],
        provider: 'facebook'
      ).first_or_initialize

      p = SecureRandom.urlsafe_base64(nil, false)
      @resource.password = p
      @resource.password_confirmation = p

      @resource.name = facebook_profile['name']
      @resource.email = facebook_profile['email']

      @token = SecureRandom.urlsafe_base64(nil, false)

      @client_id = SecureRandom.urlsafe_base64(nil, false)
      @token = SecureRandom.urlsafe_base64(nil, false)

      @resource.tokens[@client_id] = {
        token: BCrypt::Password.create(@token),
        expiry: (Time.now.utc + DeviseTokenAuth.token_lifespan).to_i
      }

      @resource.save
    end
    render json: {
      data: resource_data(resource_json: @resource.customer_token_validation_response)
    }
  end

  def resource_data(opts={})
    opts[:resource_json] || @resource.as_json
  end
end

Hope it helps.

@gibo
Copy link

gibo commented Nov 17, 2017

is there an official solution for this?

@zachfeldman
Copy link
Contributor

@gibo there is not, but we do take pull requests! We'd love to see yours.

@gibo
Copy link

gibo commented Nov 17, 2017

@zachfeldman seems like there is already a PR for this :) #793

@zachfeldman
Copy link
Contributor

Ah, I didn't realize this was the same thing! Sorry about that @gibo .

Have you tested the code in the open PR? If you've tested it along with the code that's currently in beta:
#972 (comment)

then we may merge that PR sooner rather than later.

@gibo
Copy link

gibo commented Nov 17, 2017

@zachfeldman I’ll give it a test

@gibo
Copy link

gibo commented Nov 18, 2017

I made a small change to @geoandri code, as it was not returning the correct headers

def create_and_or_sign_in_profile facebook_profile
  @resource = Profile.find_by email: facebook_profile['email']

  @client_id = request.headers['client']    

  if @resource.nil?
    @resource = Profile.create(provider: 'email', uid: facebook_profile['id'], email: facebook_profile['email'])
    p = SecureRandom.urlsafe_base64(nil, false)
    @resource.password = @resource.password_confirmation = p
    @resource.name = facebook_profile['name']
    @resource.email = facebook_profile['email']
  end

  @token = SecureRandom.urlsafe_base64(nil, false)

  @resource.tokens[@client_id] = {
    token: BCrypt::Password.create(@token),
    expiry: (Time.now.utc + DeviseTokenAuth.token_lifespan).to_i
  }

  auth_header = @resource.create_new_auth_token @client_id
  response.headers.merge!(auth_header)
  
  @resource.save

  render json: {
    data: resource_data(resource_json: @resource.token_validation_response)
  }
end

@zachfeldman
Copy link
Contributor

Nice @gibo. Want to maybe submit a clean PR if you're done testing?

@gibo
Copy link

gibo commented Nov 19, 2017

I don't think it can really be tidied up into a PR, it really seems that Omniauth cannot be used with rails-api projects and therefore devise token auth.

I think there needs to be a new gem created to handle this scenario which integrates into devise token auth.

but for the time being perhaps the code above could be put into the read me?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests