Authenticate user using omniauth and Facebook for a rails API?

后端 未结 3 1767
抹茶落季
抹茶落季 2021-01-29 19:48

I\'m building a Rails API and have successfully built a way for a user to authenticate using Omniauth Identity.

We simply post to auth/identity/callback from the client,

相关标签:
3条回答
  • 2021-01-29 20:26

    To solve this issue the best resource I found is the rails example application in the satellizer github repo : https://github.com/sahat/satellizer/tree/master/examples/server/ruby

    Your satellizer code calls the AuthController.authenticate method. This method uses the oauth model classes for each provider to convert the code you receive into an access token. Then in your user class you can retrieve the user that match the info you got from the provider.

    At the end the controller method returns the jwt token to the client.

    In my case the controller part is a bit different because I also use devise for mail/password authentication but I copy th oauth classes as is and it works like a charm.

    0 讨论(0)
  • 2021-01-29 20:37

    the best way I found (after being stuck for a while on this issue ) is to do your omniauth2 (specifically in my case using satellizer angular plugin) manually...

    I'll discuss the solution for Facebook as it was my case, but everything could apply to any other provider.

    first you have to know how omniauth2 works (as documented for humans here)...

    1. Client: Open a popup window for user to authenticate.
    2. Client: Sign in (if necessary), then authorize the application.
    3. Client: After successful authorization, the popup is redirected back to your app. with the code (authorization code) query string parameter

    the redirect back url must match your front-end app url not the back-end url and it must be specified in your facebook app configurations

    1. Client: The code parameter is sent back to the parent window that opened the popup.
    2. Client: Parent window closes the popup and sends a POST request to backend/auth/facebook with code parameter.
    3. Server: code (Authorization code) is exchanged for access token

    here is described in details how to exchange the code for an access-token from facebook developers documentation

    1. Server: use the access-token retrieved in step 6 to retrieve the User's info.

    2. VOILA you've got yourself a user you can merge/create account for/link with other oauth providers/etc. but bear in mind that user can revoke some of the permissions (like email, facebook supports revoking some of the permissions)...


    (enough talking, show me some code)

    First you have to add HTTParty gem to your Gemfile

    gem 'httparty'  # Makes http fun again (http client)
    

    I've added this gist which contains the flow for step (6, 7 and 8) those are the most problematic steps and are not documented almost anywhere.

    the gist exports 2 main methods:

    Omniauth::Facebook.authenticate(authorization_code)
    

    which is used to authenticate the user with facebook and return the user_info, long_live_access_token (valid for 60 days)

    Omniauth::Facebook.deauthorize(access_token)
    

    which is used to de-authorize/revoke the access_token and application permissions on facebook...

    This is used for special requirement I have, when the user revoke the email permission requested on facebook login... we revoke the whole application permissions... this will prompt the user in the next login as if it's his first login ( no need to go to facebook apps and manually revoke the application)...

    here is how it's used in the controller

    user_info, access_token = Omniauth::Facebook.authenticate(params['code'])
    if user_info['email'].blank?
      Omniauth::Facebook.deauthorize(access_token)
    end
    

    That's it... now if you are interested in the internals of the implementation... here is the code as seen in the gist. (added for reference) Feel free to fork it, edit it, help making it better.

    require 'httparty'
    
    module Omniauth
      class Facebook
        include HTTParty
    
        # The base uri for facebook graph API
        base_uri 'https://graph.facebook.com/v2.3'
    
        # Used to authenticate app with facebook user
        # Usage
        #   Omniauth::Facebook.authenticate('authorization_code')
        # Flow
        #   Retrieve access_token from authorization_code
        #   Retrieve User_Info hash from access_token
        def self.authenticate(code)
          provider = self.new
          access_token = provider.get_access_token(code)
          user_info    = provider.get_user_profile(access_token)
          return user_info, access_token
        end
    
        # Used to revoke the application permissions and login if a user
        # revoked some of the mandatory permissions required by the application
        # like the email
        # Usage
        #    Omniauth::Facebook.deauthorize(access_token)
        # Flow
        #   Send DELETE /me/permissions?access_token=XXX
        def self.deauthorize(access_token)
          options  = { query: { access_token: access_token } }
          response = self.delete('/me/permissions', options)
    
          # Something went wrong most propably beacuse of the connection.
          unless response.success?
            Rails.logger.error 'Omniauth::Facebook.deauthorize Failed'
            fail Omniauth::ResponseError, 'errors.auth.facebook.deauthorization'
          end
          response.parsed_response
        end
    
        def get_access_token(code)
          response = self.class.get('/oauth/access_token', query(code))
    
          # Something went wrong either wrong configuration or connection
          unless response.success?
            Rails.logger.error 'Omniauth::Facebook.get_access_token Failed'
            fail Omniauth::ResponseError, 'errors.auth.facebook.access_token'
          end
          response.parsed_response['access_token']
        end
    
        def get_user_profile(access_token)
          options = { query: { access_token: access_token } }
          response = self.class.get('/me', options)
    
          # Something went wrong most propably beacuse of the connection.
          unless response.success?
            Rails.logger.error 'Omniauth::Facebook.get_user_profile Failed'
            fail Omniauth::ResponseError, 'errors.auth.facebook.user_profile'
          end
          response.parsed_response
        end
    
    
        private
    
        # access_token required params
        # https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.3#confirm
        def query(code)
          {
            query: {
              # The authorization_code we want to exchange for the access_token
              code: code,
              # This must match the redirectUrl registerd in the facebook app.
              # You can save it to ENV['WEB_APP_URL'] if you have multiple facebook apps for development and testing
              # so you can support testing app on development and production app on production env.
              redirect_uri: "http://localhost:9000/",
              client_id: ENV['FB_APP_ID'], # Facebook appId
              client_secret: ENV['FB_APP_SECRET'], # Facebook app secret (must not exist on front-end app for security)
            }
          }
        end
      end
    end
    

    here is another nodejs tutorial implementing oauth for instagram that helped me understand how oauth2 is working (added for reference)

    0 讨论(0)
  • 2021-01-29 20:42

    To communication with facebook api. I recommend use 'omniauth-facebook' gem. You can clone this example to understand more: https://github.com/ralphos/omniauth-facebook-example

    0 讨论(0)
提交回复
热议问题