Authentication token is not working - ruby-on-rails

Not an issue with the gem but I just need some help with returning token.
I am using devise_auth_token gem in my rails-api app.
I have a route in my api app that will response
{ "url": google-oauth-login-url }
The frontend app use that route to put it in the login with google btn.
After a user click in the btn they will be redirected to google oauth page and after filling in the details they will be then redirected to the frontend home page with the google code in the url.
The frontend app will send a req to the api server with the code and the server will req to the google server again to exchange that google code for access-token, refresh token and all that.
After the server receives those token, the server again makes another req to the google server to fetch user profile.
The user is then saved to db with the access token as well.
password = Devise.friendly_token[0,10]
#resource = Employee.new({
name: user_info["displayName"],
admin: true,
first_name: CustomRegex.japanese?(user_info["name"]["givenName"]) ? '' : user_info["name"]["givenName"],
last_name: CustomRegex.japanese?(user_info["name"]["familyName"]) ? '' : user_info["name"]["familyName"],
email: user_info["emails"][0]["value"],
password: password,
password_confirmation: password
})
#client_id = SecureRandom.urlsafe_base64(nil, false)
#token = SecureRandom.urlsafe_base64(nil, false)
# #resource = Employee.find user.id
#resource.tokens[#client_id] = {
token: BCrypt::Password.create(#token),
expiry: (Time.now + DeviseTokenAuth.token_lifespan).to_i
}
#resource.skip_confirmation!
#resource.save!
# #resource.save!(validate: false)
# sign_in #resource
return render json: {client_id: #client_id, expiry: #resource.tokens[#client_id][:"expiry"],
token: #token, uid: #resource.uid
}
I used the returned cliend_id expiry, token and uid in the header to make a req to my api and it gave me not authorized error. Can you tell me what I am doing wrong?

This is how i solved my problem
skip_after_action :update_auth_header, :only => [:token_for_code]
def log_in_or_create_employee(user_info)
#resource = Employee.find_by email: user_info["emails"][0]["value"]
if #resource.nil?
password = Devise.friendly_token[0,10]
#resource = Employee.new({
name: user_info["displayName"],
admin: true,
first_name: CustomRegex.japanese?(user_info["name"]["givenName"]) ? '' : user_info["name"]["givenName"],
last_name: CustomRegex.japanese?(user_info["name"]["familyName"]) ? '' : user_info["name"]["familyName"],
email: user_info["emails"][0]["value"],
password: password,
password_confirmation: password
})
end
#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 + DeviseTokenAuth.token_lifespan).to_i
}
#resource.skip_confirmation!
#resource.save!
return render json: {client_id: #client_id, expiry: #resource.tokens[#client_id][:"expiry"],
token: #token, uid: #resource.uid
}
end

Related

Rails is returning an instance of User instead of a user Object

I recently deployed my site on Heroku and Netlify and was having issues with Auth. My current issue (and hopefully last) is that upon login, rails is sending back a user instance instead of the object with information (i.e #User:0x000056205efbbad8). I get a token from my rails response and upon refresh am logged in but am not automatically logged in because of the user instance being returned instead of an object with user information.
This is my auth controller
class AuthController < ApplicationController
def login
user = User.find_by(username: params[:username])
if user && user.authenticate(params[:password])
secret = ENV["SECRET_KEY_BASE"]
token = JWT.encode({ user_id: user.id }, secret, 'HS256')
render json: { user: UserSerializer.new(user), token: token }
else
render json: { failure: "Invalid Username or Password" }
end
end
def signup
auth_params = params.permit(:username, :password, :email, :avatar)
if params[:avatar].instance_of?(String) || params[:avatar].nil?
user = User.create(auth_params)
render json: user
else
imageUploaded = Cloudinary::Uploader.upload(params[:avatar])
user_params_new = auth_params
user_params_new[:avatar] = imageUploaded["url"]
user = User.create(user_params_new)
if user.valid?
secret = ENV["SECRET_KEY_BASE"]
token = JWT.encode({ user_id: user.id }, secret, 'HS256')
render json: {user: user, token: token }, status: :created
else
render json: { error: user.errors.full_messages }, status: :unprocessable_entity
end
end
end
end
Here is my login function on my React frontend
function handleLogin(e) {
e.preventDefault()
fetch(`${process.env.REACT_APP_API_BASE_URL}/login`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(loginData)
})
.then(r => r.json())
.then(data => {
if (data.failure) {
Swal.fire({
icon: 'error',
title: 'Oops...',
text: 'Incorrect Username or Password!'
})
} else {
setCurrentUser(data.user)
setUserReviews(data.user.reviews)
setFavorites(data.user.favorites)
localStorage.setItem("token", data.token)
history.push("/festivals")
}
})
}
I so appreciate any help on this, thanks so much!
Link to github repo: https://github.com/connormul/festie-backend
https://github.com/connormul/festie-frontend
render json: { user: UserSerializer.new(user), token: token }
This doesn't look a correct use of serializer
try to change it to
render json: { user: UserSerializer.new(user).as_json, token: token }

Getting Unable to refresh google_oauth2 authentication token error.

I am trying to get access tokens from OAuth.io for any Google based provider however whenever I authenticate I get an access_token but no refresh_token. I have chosen offline for the access_type but still no joy.
def google_auth
# Create a new API client & load the Google Drive API
client = Google::APIClient.new
client.authorization.client_id = ENV['GOOGLE_ID']
client.authorization.client_secret = ENV['GOOGLE_SECRET']
client.authorization.scope = ENV['GOOGLE_SCOPE']
client.authorization.redirect_uri = ENV['REDIRECT_URI']
client.authorization.code = self.code.chomp || ""
client.authorization.access_token = self.token
client.authorization.refresh_token = self.refresh_token
# client.authorization.additional_parameters = {
# "access_type" => "offline", # offline access
# "include_granted_scopes" => "true" # incremental auth
#
if client.authorization.refresh_token &&
client.authorization.expired?
client.authorization.fetch_access_token!
end
return client
end
def refresh_google
options = {
body: {
client_id: ENV['GOOGLE_ID'],
client_secret: ENV['GOOGLE_SECRET'],
refresh_token: self.refresh_token,
grant_type: 'refresh_token',
access_type: 'offline'
},
headers: {
'Content-Type' => 'application/x-www-form-urlencoded',
# 'access_type' =>'offline'
}
}
#response = HTTParty.post('https://accounts.google.com/o/oauth2/token', options)
if #response.code == 200
self.token = #response.parsed_response['access_token']
self.save
else
Rails.logger.error("Unable to refresh google_oauth2 authentication token.")
Rails.logger.error("Refresh token response body: #{#response.body}")
end
end
Please help in this regard
Issue resolved: I was trying to put
"client.authorization.additional_parameters = {
"access_type" => "offline", # offline access
"include_granted_scopes" => "true" # incremental auth"
in User the rb model where callback was made but had to put it under session controller where API was called!

Google Omniauth2 Rails Giving me "Example User" when testing

I am attempting to stub out an omniauth authentication hash to test my integration with RSpec. For some reason my User model is being fed an "Example User," that does not have all the info a regular signed in Google user would have.
This is the param given to User that is breaking the tests: {"provider"=>"default", "uid"=>"1234", "info"=>{"name"=>"Example User"}}
This is what it should be, and if I step to the next iteration with pry, it works:
{:provider=>"google",
:uid=>"12345678910",
:info=>{:email=>"limsammy1#gmail.com", :first_name=>"Sam", :last_name=>"Lim"},
:credentials=>{:token=>"abcdefg12345", :refresh_token=>"12345abcdefg", :expires_at=>Thu, 16 Nov 2017 15:27:23 -0700}}
Here is my spec:
require 'rails_helper'
def stub_omniauth
OmniAuth.config.test_mode = true
OmniAuth.config.mock_auth[:google] = OmniAuth::AuthHash.new({
provider: "google_oauth2",
uid: "12345678910",
info: {
email: "limsammy1#gmail.com",
first_name: "Sam",
last_name: "Lim"
},
credentials: {
token: "abcdefg12345",
refresh_token: "abcdefg12345",
expires_at: DateTime.now,
}
})
end
RSpec.feature "user logs in" do
scenario "using google oauth2 'omniauth'" do
stub_omniauth
visit root_path
expect(page).to have_link("Sign in with Google")
click_link "Sign in with Google"
expect(page).to have_content("Sam Lim")
expect(page).to have_content("Logout")
end
end
And here is my User model method:
def self.update_or_create(auth)
user = User.find_by(uid: auth[:uid]) || User.new
binding.pry
user.attributes = {
provider: auth[:provider],
uid: auth[:uid],
email: auth[:info][:email],
first_name: auth[:info][:first_name],
last_name: auth[:info][:last_name],
token: auth[:credentials][:token],
refresh_token: auth[:credentials][:refresh_token],
oauth_expires_at: auth[:credentials][:expires_at]
}
user.save!
user
end
I call that method in my sessions controller here:
def create
user = User.update_or_create(request.env["omniauth.auth"])
session[:id] = user.id
redirect_to root_path
end
I came across exactly this issue some days ago.
The problem is in def stub_omniauth.
You should change OmniAuth.config.mock_auth[:google] => OmniAuth.config.mock_auth[:google_oauth2]

Net::HTTPBadResponse (wrong header line format)

Please help me understand, what i'm doing wrong?
I need to make POST request to api from my rails app, i tried to do it firstly with gem "faraday" and then with "net/http". Response from server the same. Account creates, but gives error:
Net::HTTPBadResponse (wrong header line format)
class Connect
require 'uri'
require 'net/http'
require 'net/https'
require 'faraday'
def initialize(email, password, account)
#base_url = "https://example.com"
#client_id = ENV["client_id"]
#client_secret = ENV["client_secret"]
#email = email
#password = password
#account = account
end
# method with net/http
def create_account
#toSend = {
first_name: #account[:first_name],
last_name: #account[:last_name],
client_id: #client_id,
client_secret: #client_secret,
email: #email,
password: #password
}.to_json
uri = URI.parse("#{#base_url}/endclient/api/account")
https = Net::HTTP.new(uri.host,uri.port)
https.use_ssl = true
req = Net::HTTP::Post.new(
uri.path,
initheader = {
'Content-Type' =>'application/json',
'Accept' =>'application/json'
}
)
req.body = "#{#toSend}"
res = https.request(req)
end
# faraday method
def make_account
conn = Faraday.new(:url => "#{#base_url}") do |faraday|
faraday.response :logger
faraday.adapter Faraday.default_adapter
end
params = { first_name: #account[:first_name],
last_name: #account[:last_name],
client_id: #client_id,
client_secret: #client_secret,
email: #email,
password: #password
}.to_json
response = conn.post do |req|
req.url '/endclient/api/account'
req.headers['Content-Type'] = 'application/json'
req.headers['Accept'] = 'application/json'
req.body = params
end
end
end
Done this request with success via Postman, so endpoint of the api is working.
Please help me understand what's wrong with headers ?
Thanks in advance!
UPDATE:
May be problem in this ?
Cannot render console with content type application/jsonAllowed content types: [#<Mime::Type:0x00000003661658 #synonyms=["application/xhtml+xml"], #symbol=:html, #string="text/html">, #<Mime::Type:0x00000003660fc8 #synonyms=[], #symbol=:text, #string="text/plain">, #<Mime::Type:0x0000000365ca68 #synonyms=[], #symbol=:url_encoded_form, #string="application/x-www-form-urlencoded">]
If you want a http lib that easily mixes into a class/module than I would recommend httparty over Faraday or just plain old Net::HTTP.
require 'httparty' # Requires go at the top of the file. Not in class body.
class MyApiClient
include HTTParty
base_uri 'test.example.com'
# Tells Httparty to send JSON for all requests.
format :json
# #see https://robots.thoughtbot.com/ruby-2-keyword-arguments
def initialize(client_id: ENV["client_id"], client_secret: ENV["client_secret"])
#options = {
body: {
client_id: client_id,
client_secret: client_secret
}
}
end
# #return [HTTParty::Response]
def create_account(first_name:, last_name:, email:, password:)
# ActiveSupport method that creates a copy so that we don't
# alter the instance options as a side effect.
opts = #options.deep_dup
opts[:body].merge!(
email: email,
password: password,
first_name: first_name,
last_name: last_name
)
self.class.post('/endclient/api/account', opts)
end
end
The nice thing is that httparty will do all the work with parsing the parameters to and from JSON and sending the correct headers. It also takes care of the dull work of building URI's.
Note that the method signature is slightly different:
client = MyApiClient.new
response = client.create_account(
first_name: 'John',
last_name: 'Doe',
email: 'john.doe#example.com',
password: 'abcd1234'
)
There was a problem on API side, wrong symbols in header.

Google OAuth access tokens

I'm so confused by OAuth and Google. It took me forever to get the refresh_token to create a new access_token. Then to find out the refresh_token expires too?? What is the point of that!!!??
All I need to do is persist a valid access_token for use with legato.
Here is what I manually enter into my terminal to retrieve an OAUTH code:
client = OAuth2::Client.new('GA_CLIENT_ID', 'GA_SECRET_KEY', {
:authorize_url => 'https://accounts.google.com/o/oauth2/auth',
:token_url => 'https://accounts.google.com/o/oauth2/token'
})
client.auth_code.authorize_url({
:scope => 'https://www.googleapis.com/auth/analytics.readonly',
:redirect_uri => 'http://localhost',
:access_type => 'offline',
:approval_prompt=> 'force'
})
Then I manually enter the outputted url to in my browser. I export the returned OAUTH code as to an env variable and get the access token:
access_token = client.auth_code.get_token(ENV['GA_OAUTH_CODE'], :redirect_uri => 'http://localhost')
Then I can access the access_token and refresh_token:
begin
api_client_obj = OAuth2::Client.new(ENV['GA_CLIENT_ID'], ENV['GA_SECRET_KEY'], {:site => 'https://www.googleapis.com'})
api_access_token_obj = OAuth2::AccessToken.new(api_client_obj, ENV['GA_OAUTH_ACCESS_TOKEN'])
self.user = Legato::User.new(api_access_token_obj)
self.user.web_properties.first # this tests the access code and throws an exception if invalid
rescue Exception => e
refresh_token
end
end
def refresh_token
refresh_client_obj = OAuth2::Client.new(ENV['GA_CLIENT_ID'], ENV['GA_SECRET_KEY'], {
:authorize_url => 'https://accounts.google.com/o/oauth2/auth',
:token_url => 'https://accounts.google.com/o/oauth2/token'
})
refresh_access_token_obj = OAuth2::AccessToken.new(refresh_client_obj, ENV['GA_OAUTH_ACCESS_TOKEN'], {refresh_token: ENV['GA_OAUTH_REFRESH_TOKEN']})
refresh_access_token_obj.refresh!
self.user = Legato::User.new(refresh_access_token_obj)
end
After an hour, my tokens expire and I have to manually start the process over again from the browser! How can I replicate this in code??
Here you go, made a little something just for you :)
It's a simple implementation, specifically to ease the pain of renewing tokens.
Just be sure to:
Put in your own APP_ID and APP_SECRET.
Either only save your refresh_token and call refresh_token() every time before you use it, or use refresh_token_if_needed() every time, and re-save the token and expires_at (preferred obviously , since you'll only refresh when needed).
Let me know how it worked out.
.
require 'gmail'
require 'gmail_xoauth'
require 'httparty'
class GmailManager
APP_ID = "DDDDDDDDDDDD-SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS.apps.googleusercontent.com"
APP_SECRET = "SSSSSS-SSSSSSSSSSSSSSSSS"
def refresh_token(refresh_token)
Rails.logger.info "[GmailManager:refresh_token] refreshing using this refresh_token: #{refresh_token}"
# Refresh auth token from google_oauth2 and then requeue the job.
options = {
body: {
client_id: APP_ID,
client_secret: APP_SECRET,
refresh_token: refresh_token,
grant_type: 'refresh_token'
},
headers: {
'Content-Type' => 'application/x-www-form-urlencoded'
}
}
response = HTTParty.post('https://accounts.google.com/o/oauth2/token', options)
if response.code == 200
token = response.parsed_response['access_token']
expires_in = DateTime.now + response.parsed_response['expires_in'].seconds
Rails.logger.info "Success! token: #{token}, expires_in #{expires_in}"
return token, expires_in
else
Rails.logger.error "Unable to refresh google_oauth2 authentication token."
Rails.logger.error "Refresh token response body: #{response.body}"
end
return nil, nil
end
def refresh_token_if_needed(token, expires_on, refresh_token)
if token.nil? or expires_on.nil? or Time.now >= expires_on
Rails.logger.info "[GmailManager:refresh_token_if_needed] refreshing using this refresh_token: #{refresh_token}"
new_token, new_expires_on = self.refresh_token(refresh_token)
if !new_token.nil? and !new_expires_on.nil?
return new_token, new_expires_on
end
else
Rails.logger.info "[GmailManager:refresh_token_if_needed] not refreshing. using this token: #{token}"
end
return token, expires_on
end
end

Resources