Skip to content
This repository has been archived by the owner on Mar 22, 2021. It is now read-only.

Using multiple authentication strategies #11

Closed
anthonator opened this issue Sep 25, 2015 · 11 comments
Closed

Using multiple authentication strategies #11

anthonator opened this issue Sep 25, 2015 · 11 comments
Labels

Comments

@anthonator
Copy link

How would you provide authentication using email/password and facebook login?

@nsarno
Copy link
Owner

nsarno commented Sep 25, 2015

Could you give more details about what you are trying to achieve? Thanks

@anthonator
Copy link
Author

I want the user to have the options of logging in with an email/password or a social account (Facebook).

I store the social account info differently than the email/password.

Didn't look like there was a way to differentiate the type of credentials using knock.

@nsarno
Copy link
Owner

nsarno commented Sep 25, 2015

By more details I meant are you using knock inside a rails API interacting with a single page javascript application? Something else? If you could try to describe more precisely what you already have and what you are missing I could give you better answers.

This is how you can customize how you retrieve the user when signing in:

inside config/initializers/knock.rb:

  ## Current user retrieval from handle when signing in
  ## --------------------------------------------------
  ##
  ## This is where you can configure how to retrieve the current user when
  ## signing in.
  ##
  ## Knock uses the `handle_attr` variable to retrieve the handle from the
  ## AuthTokenController parameters. It also uses the same variable to enforce
  ## permitted values in the controller.
  ##
  ## You must raise ActiveRecord::RecordNotFound if the resource cannot be retrieved.
  ##
  ## Default:
  # self.current_user_from_handle = -> (handle) { User.find_by! Knock.handle_attr => handle }

Note that this will be deprecated in future versions of Knock in favour of a method in the user model. But the general idea will stay the same.

Is this helping you? If not, don't hesitate to provide code samples or anything that could help me understand better what your exact use case is.

I never implemented OAuth with knock myself, but it would be interesting to see if there is a way to provide something in future versions that would make it easier.

@anthonator
Copy link
Author

I will be using Knock for an SPA and 2 mobile apps.

The problem is that I'm storing the social ids and the email and password differently. So the fact that the authenticate method only provides a password parameter doesn't give me the context I need to determine whether I should be checking against a social id or a crypted password.

I'm not actually storing the OAuth token as I don't really need to interact with their API other than to log them in.

Now, I understand that I may not be approaching this correctly which is why I'm having issues thinking through this.

I appreciate the help!

@nsarno
Copy link
Owner

nsarno commented Sep 26, 2015

Social authentication is out of the scope of knock for the moment. Although it is definitely something I'd like to implement in the future.

For the moment, what you can do is implement your own controller that retrieves (or creates) a user from social authentication and responds with a JWT token.

Here's how you can generate a JWT token using Knock:

# Create the token
knock_token = Knock::AuthToken.new payload: { sub: user.id }

# Access the JWT token
knock_token.token

The "flow" would probably be something similar to this:

social-auth-flow

(source: http://madhatted.com/2014/6/17/authentication-for-single-page-apps)

Hope this helps! And please let us know how you ended up implementing this :)

@nsarno nsarno closed this as completed Sep 26, 2015
@anthonator
Copy link
Author

Honestly, I see this as a flexibility issue and not something specific to social auth.

If you were to make params available when finding and authenticating a user you'd provide a lot more flexibility.

I'm excited to see where this gem goes. I would love to see a solid JWT implementation for Rails.

Thanks again for putting time into my problem. I'll give your suggestion a shot.

I really appreciate the help!

@nsarno
Copy link
Owner

nsarno commented Sep 26, 2015

The v2 is already providing more flexibility as you can access the params to retrieve a user if you need to. Have a look at the changelog if you're interested! This has not been released yet and it is still subject to change.

@anthonator
Copy link
Author

Awesome! I'll give v2 a look. Would you say it's beta? Alpha?

@nsarno
Copy link
Owner

nsarno commented Sep 26, 2015

Alpha, it's stable but the design could change.
On Sun, Sep 27, 2015 at 01:51 Anthony Smith notifications@github.com
wrote:

Awesome! I'll give v2 a look. Would you say it's beta? Alpha?


Reply to this email directly or view it on GitHub
#11 (comment).

@muZk
Copy link

muZk commented Nov 3, 2016

Hey, I created a sample application for facebook login + jwt. I'm not sure if its the best approach but it works: https://github.com/muZk/rails5-api-jwt-facebook-auth

It was designed for getting the FB accessToken "client" side (which could be a JS application or Mobile app).

@nsarno any thoughts?


The key files are:

facebook_user_token_controller.rb

class FacebookUserTokenController < ActionController::API

  before_action :authenticate

  def create
    render json: auth_token, status: :created
  end

  private

  def authenticate
    unless entity.present?
      raise Knock.not_found_exception_class
    end
  end

  def auth_token
    if entity.respond_to? :to_token_payload
      Knock::AuthToken.new payload: entity.to_token_payload
    else
      Knock::AuthToken.new payload: { sub: entity.id }
    end
  end

  def entity
    @entity ||=
      if FacebookService.valid_token?(auth_params[:access_token])
        data = FacebookService.fetch_data(auth_params[:access_token])
        User.find_or_create_by uid: data['id'] do |user|
          user.first_name = data['first_name']
          user.last_name = data['last_name']
          user.email = data['email']
        end
      end
  end

  def auth_params
    params.require(:auth).permit :access_token
  end

end

And facebook_service.rb which has the logic for facebook token validation and data retrieval:

class FacebookService

  def self.valid_token?(access_token)
    status = false
    begin
      # We need to check if the access_token is valid for our FB APP. Source: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#checktoken
      debug_token = Koala::Facebook::API.new(access_token).debug_token(app_access_token_info['access_token'])
      status = true if debug_token['data']['is_valid']
    ensure
      return status
    end
  end

  def self.fetch_data(access_token)
    Koala::Facebook::API.new(access_token).get_object('me', fields: 'name,first_name,last_name,email') if valid_token?(access_token)
  end

  def self.app_access_token_info
    @app_access_token ||= Koala::Facebook::OAuth.new.get_app_access_token_info
  end

end

@FunkyloverOne
Copy link

FunkyloverOne commented Dec 29, 2017

@muZk it seems that you have mixed things up here in this line:

debug_token = Koala::Facebook::API.new(access_token).debug_token(app_access_token_info['access_token'])

You should be using app_acces_token for Koala::Facebook::API.new and given regular user's acces_token for debug_token method, like this:

debug_token = Koala::Facebook::API.new(app_access_token_info['access_token']).debug_token(access_token)

fkotsian added a commit to CottageClass/cottageclass-app-api that referenced this issue Oct 12, 2018
- debug_token was failing due to incorrect access token and hidden by
ensure block
- nsarno/knock#11, last comment is correct,
fetch debug token using app_access_token to allow root access to FB API
muZk added a commit to muZk/knock that referenced this issue Jul 10, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

4 participants