I'm struggling to find a working method to implement oauth2 login via Facebook & Google for my existing api-only rails app. Login flow & jwt management is done with Devise & Doorkeeper, following this guide.
I tried with Doorkeeper-grants-assertion examples, but none of them is working. The problem i have is that i can't exchange the provider's token with my jwt token.
Client side (Android and iOS apps) i can login with provider and get the token, but when i try to authorize the user to create a new token, it gives me errors.
The code is the same as examples. In the case of Google i'm skipping token request because i can already get it from client:
class GoogleController
def initialize(auth_code)
@auth_code = auth_code
@user_data = user_data
end
def user_data
url = "https://www.googleapis.com/oauth2/v3/userinfo?access_token=" + @auth_code
response = Faraday.get(url, {:Accept => 'application/json'})
@resp = JSON.parse(response.body)
end
def email
@resp['email']
end
def first_name
@resp['first_name']
end
def last_name
@resp['last_name']
end
def get_user!
# 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 = User.find_by(email: email)
if user
Rails.logger.info "User"
user
else
user = User.new(email: email, password: Devise.friendly_token.first(10))
user.save
Rails.logger.info "No User"
user
end
end
end
I'm using postman to make requests, below there is the response if my body is:
{
"client_id": "doorkeeper_app_uid",
"client_secret": "doorkeeper_app_secret",
"grant_type": "assertion",
"provider": "google",
"assertion": "MY USER TOKEN" }
{ "error": "invalid_client",
"error_description": "Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method." }
I just found out i didn't return an User object, that's why Facebook didn't work. Now, with the same code, only different token endpoint, Facebook login is working and i can find or create the user and return the jwt token, while Google is not.
If someone could point me in the right direction it would be great.
EDIT
after further investigation i'm at this point: i can find or create my Google authenticated user, but when i return it to doorkeeper assert grant extension, it fails validation
def validate_resource_owner
!resource_owner.nil?
end
in class
PasswordAccessTokenRequest
and i can't generate new jwt token. What's different from facebook that makes this validation to fail?
Incredible guys, mystical things happens but i've found a solution.
Somehow there was a conflict with 2 providers in doorkeeper.rb
initializer if written like so: (Don't do this)
resource_owner_from_assertion do
if provider == "facebook"
g = Api::V1::FacebookController.new(params[:assertion])
g.get_user!
end
if provider == "google"
g = Api::V1::GoogleController.new(params[:assertion])
return g.get_user!
end
end
instead do something like:
resource_owner_from_assertion do
provider = params[:provider]
controller = (provider == "facebook") ? Api::V1::FacebookController.new(params[:assertion]) : Api::V1::GoogleController.new(params[:assertion])
controller.get_user!
end
Then there was another issue inside controllers, because i used "user" as variable name:
user = User.find_by(email: email)
and this is apparently bad, so use
facebook_user = User.find_by(email: email)
Now everything seems to work as it is supposed to. I hope someone will find this useful.