-
Notifications
You must be signed in to change notification settings - Fork 53
Home
The general scenario for using this gem assumes you are running a web app on the server that tries to authenticate a resource against a 3rd party Oauth2 provider. Depending on the Oauth2 provider, there may be some limitations when using a standard authentication endpoint (eg. Google limits the possibility of authenticating from non-standard locations /aka. IPs/, which is probable as your app server is located elsewhere then the client). Therefore, appropriate workflow should be implemented - examples for the most popular providers can be found below.
- Client app (browser client, mobile app) obtains a short living
auth code
for the required scope. - The
auth_code
and provider name is send to our application:
$.post 'https://domain.com/oauth2/token',
client_id: d017e2ecd288d3141478af6667d9b7e1401e40b9c8e6290c52ad345dbd7b99fe
client_secret: 67ac711924cc8c46cd4395c13cfe4af91eda7408dc27afbf030d3492445b02ffc
grant_type: assertion
provider: <provider_name>
assertion: <auth_code>
- Server calls 3rd party OAuth2 endpoint and exchanges
auth_code
foraccess_token
, that can be used to obtain user data, allowed by the scope.
We will assume the provider-specific code is defined in a separate classes (eg. ExternalAuth::Google
), which examples you will find below.
The strategy described in the introduction is fully supported by Google as CrossClientAuth. Please follow the steps in the documentation provided by google (setting up an application in the Google API console, create server API key). The application should request auth code (one time authorization code) for user from Google API (which can be later exchanged for access token by the server) requiring the following permissions:
- profile
The following request can be then constructed:
$.post 'https://domain.com/oauth2/token',
client_id: d017e2ecd288d3141478af6667d9b7e1401e40b9c8e6dd290c52ad345bd7b99fe8
client_secret: 67ac711924cc8c46cd4395c13cfe4af91eda740dd8dc27afbf0303492445b02ffc
grant_type: assertion
provider: google
assertion: <auth_token>
Then we need to add the following declaration to config/initializers/doorkeeper.rb
:
resource_owner_from_assertion do
provider = params[:provider]
if provider == "google"
g = ExternalAuthorize::Google.new(params[:assertion])
g.get_user!
end
end
And the class itself might look like:
# extenal_auth_google.rb
module ExternalAuthorize
class Google
def initialize(access_code)
@access_code = access_code
@user_data = user_data
end
def app_auth_data
return {client_id: Rails.application.secrets[:google_api_key], client_secret: Rails.application.secrets[:google_api_secret]}
end
def fetch_access_token
if @access_token.present?
@access_token
else
uri = URI.parse("https://www.googleapis.com/oauth2/v3/token")
params = {
client_id: app_auth_data[:client_id],
client_secret: app_auth_data[:client_secret],
grant_type: 'authorization_code',
code: @access_code
}
response = Net::HTTP.post_form(uri, params)
data = JSON.parse(response.body)
if data['access_token'].present?
@access_token ||= data['access_token']
else
puts data
nil
end
end
end
def user_data
access_token = fetch_access_token
if access_token.present?
google = URI.parse('https://www.googleapis.com/plus/v1/people/me?access_token=' +
access_token)
response = Net::HTTP.get_response(google)
JSON.parse(response.body)
else
nil
end
end
def get_user!
if @user_data.present?
# below you should implement the logic to find/create a user in your app basing on @user_data
# It should return a user object
User.authorize_from_external(@user_data)
else
nil
end
end
end
end
Unlike google, facebook does not offer any special scenario for server app authorization on behalf of the user.
The application should authenticate user against Facebook Graph API requiring the following permissions:
- user_about_me
- user_birthday (as stated in https://developers.facebook.com/tools/explorer/)
As a result, app should get valid OAuth2 access token, that should be used to authenticate user against this API. The parameters obtained from the given access token can be checked by querying: GET https://graph.facebook.com/me?access_token=<access_token>. Please ensure that the email is returned in the response.
The following request can be then constructed:
$.post 'https://domain.com/oauth2/token',
client_id: d017e2ecd288d3141478af6667d9b7e1401e40b9c8e6dd290c52ad345bd7b99fe8
client_secret: 67ac711924cc8c46cd4395c13cfe4af91eda740dd8dc27afbf0303492445b02ffc
grant_type: assertion
provider: facebook
assertion: <access_token>
Then we need to add the following declaration to config/initializers/doorkeeper.rb
:
resource_owner_from_assertion do
provider = params[:provider]
if provider == "facebook"
g = ExternalAuthorize::Facebook.new(params[:assertion])
g.get_user!
end
end
And the class itself might look like:
# require 'google/api_client'
module ExternalAuthorize
class Facebook
def initialize(auth_code)
@auth_code = auth_code
@user_data = user_data
end
def user_data
facebook = URI.parse("https://graph.facebook.com/v2.5/me?access_token=#{@auth_code}&fields=id,name,email,first_name,last_name,gender")
response = Net::HTTP.get_response(facebook)
JSON.parse(response.body)
end
def user_data_req
"https://graph.facebook.com/v2.5/me?access_token=#{@auth_code}&fields=id,name,email,first_name,last_name,gender"
end
def image
img = URI.parse("https://graph.facebook.com/v2.5/me/picture?access_token=#{@auth_code}&width=180&height=180&redirect=false")
response = Net::HTTP.get_response(img)
JSON.parse(response.body)
end
def get_user!
if user_data.present?
# below you should implement the logic to find/create a user in your app basing on @user_data
# It should return a user object
User.authorize_from_external(@user_data)
else
nil
end
end
end
end