How to generate query string hash with query parameters - ruby-on-rails

I am sending API requests to JIRA using JWT token authentication. I added the method (get, post, etc.) and the endpoint to SHA256 encoding. This succeeds:
qsh = Digest::SHA256.hexdigest("GET&#{endpoint}&")
jwt = JWT.encode({
qsh: qsh,
iat: issued_at,
exp: expires_at,
iss: key
}, secret)
However, I cannot add query parameters to the URI. If I append query parameters:
qsh = Digest::SHA256.hexdigest("GET&#{endpoint}&start=50&limit=50")
jwt = JWT.encode({
qsh: qsh,
iat: issued_at,
exp: expires_at,
iss: key
}, secret)
I receive unauthorized response 401.
MVP:
jira_request(:get,"/rest/servicedeskapi/servicedesk/#{serviceDeskId}/organization", nil)
def jira_request(method, endpoint, data)
request = Typhoeus::Request.new(jira_rest_api_url(method, endpoint),
followlocation: true, method: method,
body: data ? data.to_json : nil,
headers: { 'X-ExperimentalApi' => 'opt-in',
'Content-Type' => 'application/json' })
request.on_complete do |response|
if response.success? && !response.body.blank?
return JSON.parse(response.body)
elsif response.code == 204
return true
else
return false
end
end
request.run
end
# Creating JWT token to Auth for each request
def jira_rest_api_url(method, endpoint)
# Gets the ADDON details for generating JWT token
jwt_auth = MyJToken.first
issued_at = Time.now.utc.to_i
expires_at = issued_at + 500
qsh = Digest::SHA256.hexdigest("#{method.to_s.upcase}&#{endpoint}&")
jwt = JWT.encode({ qsh: qsh,
iat: issued_at,
exp: expires_at,
iss: jwt_auth.key
}, jwt_auth.secret)
# return the service call URL with the JWT token added
"#{jwt_auth.api_base_url}#{endpoint}?jwt=#{jwt}"
end
end

The parameters that are hashed in:
qsh = Digest::SHA256.hexdigest("GET&#{endpoint}&limit=50&start=50")
should be added in the request url:
"#{jwt_auth.api_base_url}#{endpoint}?jwt=#{jwt}&start=50&limit=50"

Related

Apple Auth not working on Ruby on Rails Backend

I need to authentificate user with Apple Auth in mobile application. I got IdentityToken, User and AuthCode from api request from mobile app.
I need to authentificate user and get its email. My class AppleId always returns: "invalid_client".
Maybe i have some mistakes in my logic?
class AppleId
URL = 'https://appleid.apple.com/auth/token'
ISSUER = 'https://appleid.apple.com'
TEAM_ID = '6Z******38'
KEY_ID = '33******KP'
IDENTIFIER = 'ru.k*************on'
PRIVATE_KEY = <<-PEM
-----BEGIN PRIVATE KEY-----
MIGTA**********z7
-----END PRIVATE KEY-----
PEM
def initialize
#private_key = OpenSSL::PKey::EC.new PRIVATE_KEY
end
def authenticate(code)
make_request(code)
end
private
def make_request(code)
uri = URI.parse(URL)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri.request_uri)
params = {
client_id: IDENTIFIER,
client_secret: client_secret_jwt,
code: code,
grant_type: 'authorization_code'
}
request.set_form_data(params)
response = http.request(request)
response.body
end
def client_secret_jwt
jwt = JSON::JWT.new(
iss: TEAM_ID,
aud: ISSUER,
sub: IDENTIFIER,
iat: Time.now,
exp: 1.minutes.from_now
)
jwt.kid = KEY_ID
JWT.encode jwt, #private_key, 'ES256'
end
end
In my Controller:
authCode = params[:auth_code]
AppleId.new.authenticate(authCode)
=> "{\"error\":\"invalid_client\"}"

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!

Authentication token is not working

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

How to use Azure ML API with Ruby on Rails

I am using the code with a URL and API key but every time i will get the some error of 405 or 400. Is there any proper way to implement Azure ML API in Rails.
The code as below :-
data = {
"Inputs" => {
"input1" =>
{
"ColumnNames" => #a,
"Values" => [ #writer ]
}, },
"GlobalParameters" => {
}
}
body = data.to_json
puts "adssssssssssssssssssssssssssssss#{body}"
url = "https://ussouthcentral.services.azureml.net/workspaces/5aecd8f887e64999a9c854d724e5/services/5f350fa1b48647ce95c5279eee2170d0/execute?api-version=2.0&details=true"
api_key = 'wGMMQGYlo4tttV+oTjrR/tyt6xYSmWskCezNKkbGwvAVt0wsessJUORQ==' # Replace this with the API key for the web service
headers = {'Content-Type' => 'application/json', 'Authorization' => ('Bearer '+ api_key)}
url = URI.parse(url)
req = Net::HTTP::Get.new(url.request_uri,headers)
http = Net::HTTP.new(url.host, url.port)
res = http.request(req)
{"Inputs":{"input1":{"ColumnNames":["encounter_id","patient_nbr","Fname","Lname","Email","Type","race","gender","Birth Date","Birth Year","age","Age Min","Age Max","weight","admission_type_id","discharge_disposition_id","admission_source_id","time_in_hospital","payer_code","medical_specialty","num_lab_procedures","num_procedures","num_medications","number_outpatient","number_emergency","number_inpatient","number_diagnoses","max_glu_serum","A1Cresult","metformin","repaglinide","nateglinide","chlorpropamide","glimepiride","acetohexamide","glipizide","glyburide","tolbutamide","pioglitazone","rosiglitazone","acarbose","miglitol","troglitazone","tolazamide","examide","citoglipton","insulin","glyburide-metformin","glipizide-metformin","glimepiride-pioglitazone","metformin-rosiglitazone","metformin-pioglitazone","change","diabetesMed","readmitted"],"Values":[[[{"$oid":"56b1ab886e75720ba23b5400"},"","Rana","Warhurst",null,"Patient","Caucasian","Male","2012-10-23","",3,"","","","",null,null,null,"",null,"","","","","","",null,"","No","NO"]]]}},"GlobalParameters":{}}
Using Unirest gem
url = "url for ml"
api = "ml API key"
headers = "same as above"
response = Unirest.post url, headers: headers, parameters: body
response.code
response.headers
response.body
response.raw_body
The result values will be stored in response.body

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