Accessing Google Calendar using Oauth via Ruby Sinatra app - ruby

I'm building a Slack bot, with a backend Sinatra app. The function of the bot is to add and read entries from Google Calendar.
I'm using session and environment variables.
I'm unable to open up the authorization webpage for Oauth and get access to user's Calendar data. Please help!
Here's my code:
#The redirect_url entered in Google Console.
#Google Oauth redirects to this endpoint once user has authorised request.
get '/oauthcallback' do
client = Signet::OAuth2::Client.new({
client_id: ENV['CALENDAR_CLIENT_ID'],
client_secret: ENV['CALENDAR_CLIENT_SECRET'],
authorization_uri: 'https://accounts.google.com/o/oauth2/auth',
redirect_uri: "https://agile-stream-68169.herokuapp.com/oauthcallback",
code: params[:code]
})
response = client.fetch_access_token!
session[:access_token] = response['access_token']
redirect url_for(:action => :calendars)
end
#The Authorization url which checks if credentials are valid.
get '/authorize' do
# NOTE: Assumes the user is already authenticated to the app
user_id = request.session['user_id']
credentials = authorizer.get_credentials(user_id, request)
if credentials.nil?
redirect authorizer.get_authorization_url(login_hint: user_id, request: request)
end
end
#METHOD: Initialising the API by creating a Calendar Service
def intialize_api
$service = Google::Apis::CalendarV3::CalendarService.new
$service.client_options.application_name = ENV['CALENDAR_APPLICATION_NAME']
$service.authorization = auth_calendar
end
def auth_calendar
client = Signet::OAuth2::Client.new({
client_id: ENV['CALENDAR_CLIENT_ID'],
client_secret: ENV['CALENDAR_CLIENT_SECRET'],
authorization_uri: 'https://accounts.google.com/o/oauth2/auth',
scope: Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY,
redirect_uri: "https://agile-stream-68169.herokuapp.com/oauthcallback"
})
redirect client.authorization_uri.to_s
end

Related

Ruby Oauth2 Signet Return token with refresh token

I am accessing gmail api. I am using ruby to get the access token. However, I need to get a refresh token with the access token. I am using signet as suggested here.
https://readysteadycode.com/howto-access-the-gmail-api-with-ruby
The code looks like this
client = Signet::OAuth2::Client.new({
client_id: ENV.fetch('GOOGLE_API_CLIENT_ID'),
client_secret: ENV.fetch('GOOGLE_API_CLIENT_SECRET'),
token_credential_uri: 'https://accounts.google.com/o/oauth2/token',
redirect_uri: url_for(:action => :callback),
code: params[:code]
})
// this is undefined
refre_token = client.refresh_token
response = client.fetch_access_token!
access_token = response['access_token']
I tried adding additional parameters like this as suggested here
additional_parameters: {
"access_type" => "offline", # offline access
"include_granted_scopes" => "true", # incremental auth
}
to the client.new call but did not seem to have any effect.
How do I get the refresh token from Signet::OAuth2?
The refresh token will only appear the first time you are authenticated.
Follow these steps to remove the stored access so you can be authenticated like the first time.
Manage your google account -> Security -> Manage 3rd party access -> your app name -> remove access
Note: From my testing, it does not appear that the additional parameters property is necessary.

Okta Authentication works but Get User by Id gives Invalid Token Provided

I have a Django app that authenticates using Okta:
headers = {
'Authorization': 'SSWS {}'.format(<okta api token>),
'Accept': 'application/json',
'Content-Type': 'application/json'
}
authentication_payload = {
'username': <username>,
'password': <password>
}
response = requests.post(
<okta auth endpoint>,
headers=headers,
data=json.dumps(authentication_payload)
)
This works successfully. From the response content I am able to get the User Id:
content = json.loads(r.content.decode('utf-8'))
okta_user_id = content['_embedded']['user']['id']
I then use the okta_user_id to create the endpoint to get the okta user by id:
okta_user_endpoint = https://<org>.okta.com/api/v1/users/<okta_user_id>
I then use the same headers from the authentication call, with the same api token, and try to get the user by id:
user_response = requests.get(
okta_user_endpoint,
headers=headers
)
But this is unsuccessful. I get a 401 error with the following content:
{
"errorCode":"E0000011",
"errorSummary":"Invalid token provided",
"errorLink":"E0000011",
"errorCauses":[]
}
Seems straight forward with an invalid token, but if the token is invalid how am I able to successfully make the authentication call? And if the token if valid for the authentication call why is it not working to get the user by id?
Okta recently changed the way that the /authn endpoint works. The /authn endpoint no longer requires an authentication token. This was done in order to support single-page applications.
It looks like your application will need to be able to fetch user information on an arbitrary user. In that case, using an Okta API token makes sense.
However, if you were making that call from a single-page application, you would want to make a request to the /users/me API endpoint.

Offline access to google contacts using the oauth2 ruby gem

I'm trying to get offline access to google contacts using the oauth2 ruby Gem (https://github.com/intridea/oauth2). My current code is:
#!/usr/bin/env ruby
require 'oauth2'
require 'yaml'
require 'pp'
auth = YAML.load_file('.auth.yml')
client_id = auth['google']['clientid']
client_secret = auth['google']['secret']
redirect = 'urn:ietf:wg:oauth:2.0:oob'
code = ARGV[0]
token = nil
client = OAuth2::Client.new(client_id, client_secret, site: 'https://accounts.google.com', token_url: '/o/oauth2/token', authorize_url: '/o/oauth2/auth')
if code
token = client.auth_code.get_token(code, :redirect_uri => redirect)
auth['google']['token'] = token.to_hash
open('.auth.yml', 'w'){|f| f.write(auth.to_yaml)}
elsif auth['google']['token']
token = OAuth2::AccessToken.from_hash(client, auth['google']['token'])
token.refresh! if token.expired?
auth['google']['token'] = token.to_hash
open('.auth.yml', 'w'){|f| f.write(auth.to_yaml)}
else
puts client.auth_code.authorize_url(scope: 'https://www.google.com/m8/feeds', redirect_uri: redirect, access_type: :offline)
end
pp token.expired? if token
I can use this to get an access token, valid for one hour, and I do see a refresh_token in the response I get when I submit the code I get from the confirmation screen at the generated URL, but the confirmation screen only prompts me for access to my contacts, it does not ask whether I want to give offline access, and the token does indeed expire after an hour. What am I doing wrong?
Every Access token expires after one hour. when you don't request a offline, the user has to grant access again in order to get a new access token.
When requesting offline access token, as you mentioned, you will receive a refresh token. You will use this refresh token to request a new access token after or before it expires. This is done so the user wont be bothered every hour to grant access.
Here is the documentation about refresh token: https://developers.google.com/identity/protocols/OAuth2WebServer#refresh

google drive ruby api login with oauth2, what is "authorization code"

I try to use login_with_oauth2 with google_drive, but I can't understand what is authorization code in #google_drive.rb doc
client = OAuth2::Client.new(
"522807807986-gjotv2np4tdqp4do8sq0gds0p2bqugtf.apps.googleusercontent.com",
'fmWlfzejvx_UtS3CKq2Sl-WQ',
:site => "https://accounts.google.com",
:token_url => "/o/oauth2/token",
:authorize_url => "/o/oauth2/auth"
)
auth_url = client.auth_code.authorize_url(
:redirect_uri => "urn:ietf:wg:oauth:2.0:oob
http://localhost"
)
# Redirect the user to auth_url and get authorization code from redirect URL.
authorization_code = ''
auth_token = client.auth_code.get_token(
authorization_code, :redirect_uri => "urn:ietf:wg:oauth:2.0:oob
http://localhost")
session = GoogleDrive.login_with_oauth(auth_token.token, 'http://localhost:8087')
OAuth2 is the two-steps authorization mechanism. AFAIU, you have your code simply copy-pasted from official documentation, but unfortunately that’s not a working code. It’s just an example. Take a look at the comment marked red there: it’s very important:
# Redirect the user to auth_url and get authorization code from redirect URL.
You are to specify the correct pingback URI for Google server to send the response with your authorization_code (now there states localhost, which is problematically for Google to understand your IP to send you feedback.)
The summing up: you provide Google with credentials, it replies with JSON, containing your authorization_code to the address you specified. After this phase is successfully completed, you may proceed requesting auth_token.

"Error validating client secret." 404 with Facebook Oauth and ruby

I am trying to implement facebook authentication for an app with warden, after the user allows facebook auth and redirects to my app callback with the token I get a 400 while consuming the api. My warden strategy is this:
class Facebook < Warden::Strategies::Base
def client
#client ||= OAuth2::Client.new MyApp::Facebook::AppID, MyApp::Facebook::AppSecret, :site => 'https://graph.facebook.com'
end
def params
#params ||= Rack::Utils.parse_query(request.query_string)
end
def authorize_url
client.web_server.authorize_url :redirect_uri => request.url, :scope => 'email,publish_stream'
end
def authenticate!
throw(:halt, [302, {'Location' => authorize_url}, []]) unless params['code']
facebook = client.web_server.get_access_token params['code'], :redirect_uri => request.url
rescue OAuth2::HTTPError => e
puts e.response.body
end
end
Strategies.add :facebook, Facebook
The result of printing the response body is this:
{"error":{"type":"OAuthException","message":"Error validating client secret."}}
I am pretty shure the app id and app secret are the ones provided by FB.
Thanks.
I've seen that error message many times. Here are the things I would double check:
your domain is the same as what you listed in the facebook callback url
the app id is correct (actually print this out on a page, sometimes y
the app secret is correct
Add redirect_uri while creating the object of facebook that will fix the issue.
Redirect the user to https://www.facebook.com/dialog/oauth?client_id=YOUR_APP_ID&redirect_uri=YOUR_URL
After user click allow, it'll hit our Redirect Uri
At that point we'll get the code and we need to do a server side HTTP Get to the following Url to exchange the code with our oAuth access token:
https://graph.facebook.com/oauth/access_token?
client_id=YOUR_APP_ID&redirect_uri=YOUR_URL&
client_secret=YOUR_APP_SECRET&code=THE_CODE_FROM_ABOVE
Now at step 3, I kept on getting Http 400 response back.
So after some research, I found out that on that redirect_uri that we submitted on step 3 doesn't do anything but validate the request. Thus, the value need to match with step 2.
I also get the same error and i resolved by doing as below:
double check your client_id, client_secret, redirect_uri.
Add Accept: "application/json" header to thye request
fetch(
`https://graph.facebook.com/v15.0/oauth/access_token?client_id=${process.env.FACEBOOK_APP_ID}&redirect_uri=${process.env.FACEBOOK_REDIRECT_URI}&client_secret=${process.env.FACEBOOK_APP_SECRET}&code=${code}`,
{
method: "GET",
headers: {
Accept: "application/json",
},
}
)

Resources