Slack bot scope not updating - slack-api

I created a Slack bot using the "New App". Set it as a bot and am using the "OAuth Tokens for Your Workspace". With the following scopes under "Bot token scopes":
app_mentions:read
chat:write
chat:write.customize
files:read
files:write
groups:write
I added some of these permissions after the initial install (if it matters and you have to refresh something?). The bot is installed to a workspace and I have invited it to a private channel.
Using the example of the Slack API documentation:
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
slack_token = SLACK_TOKEN # xoxb-....
client = WebClient(token=slack_token)
try:
"""
response = client.files_upload(
channels="C040W4L2HA6",
file="test.txt",
title="Test upload"
)
"""
response = client.chat_postMessage(
channel="C040W4L2HA6",
text="Hello from your app! :tada:"
)
except SlackApiError as e:
# You will get a SlackApiError if "ok" is False
assert e.response["error"]
For both the file write and message examples I get: "ok":false,"error":"missing_scope","needed":"chat:write:bot","provided":"app_mentions:read" but "needed" changes from "chat:write:bot" to "file:write" if I run that part.
Few issues here. I am using the newest version of the client with Python 3.9 and it asks for a permission that is depricated (write:bot is just write now). In both cases it is as if my only active scope is app_mentions:read, nothing else. When I added the other scopes, I clicked "Request to install" and the person reviewing that accepted.
YAML config:
display_information:
name: BotName
description: Bot desc.
background_color: "#000000"
features:
bot_user:
display_name: botname
always_online: false
oauth_config:
scopes:
user:
- chat:write
bot:
- app_mentions:read
- chat:write
- files:write
- files:read
- groups:write
- chat:write.customize
settings:
interactivity:
is_enabled: true
org_deploy_enabled: false
socket_mode_enabled: true
token_rotation_enabled: false
Any ideas?

Under "Install app" I had to reinstall it to the workspace, it was not done automatically.

Related

Jenkins RoleBasedAuthorizationStrategy with OIDC and externally managed role assignments

I am trying to find a way to use the roles I manage in Okta to work with Jenkins. I have been trying to get role-strategy plugin to work, but when I log in, I get <user> is missing the Overall/Read permission.
My configuration includes OIDC for Okta, using oic-auth plugin, and I am specifying the authorizationStrategy config via JCasC. For example I have this on my JCasC okta.yaml:
authorizationStrategy:
roleBased:
roles:
global:
- name: "my_okta_group"
permissions:
- "Overall/Administer"
I see this properly getting converted into $JENKINS_HOME/config.xml.
What I don't specify, but seems to be required, is assignments:, because - it's RBAC after all, and the user assignments into roles should be made in Okta, not Jenkins.
After browsing the plug-in code, it appears hard-wired for an explicit user SID list to be specified in the JCasC. That's not going to work for me.
Is there a different way to use this plug-in to achieve the goal?
Is there another RBAC plug-in that would work better?
The above is very close to a working solution. assignments: is indeed required, and it supports username or group. Also the 'name' of 'authorizationStrategy.roleBased.roles' is the Jenkins role name, not the OIDC group name.
The oic-auth configuration maps OIDC token fields to Jenkins user properties, and then the role-strategy checks for the OIDC token's username, as well as its group(s).
My working config is:
jenkins:
securityRealm:
oic:
clientId: "${clientId}"
clientSecret: "${clientSecret}"
wellKnownOpenIDConfigurationUrl: "${oidcIssuerUrl}/.well-known/openid-configuration"
userInfoServerUrl: "${oidcIssuerUrl}/oauth2/v1/userinfo"
tokenFieldToCheckKey: ""
tokenFieldToCheckValue: ""
fullNameFieldName: "name"
groupsFieldName: "groups"
disableSslVerification: false
logoutFromOpenidProvider: true
endSessionEndpoint: "${oidcIssuerUrl}/oauth2/v1/logout"
postLogoutRedirectUrl: "https://${dns_name_full}"
escapeHatchEnabled: false
escapeHatchUsername: ""
escapeHatchSecret: "my-unused-password"
escapeHatchGroup: ""
automanualconfigure: "auto"
emailFieldName: "email"
userNameField: "name"
tokenServerUrl: "${oidcIssuerUrl}/oauth2/v1/token"
authorizationServerUrl: "${oidcIssuerUrl}/oauth2/v1/authorize"
scopes: "address phone openid profile offline_access groups email"
authorizationStrategy:
roleBased:
roles:
global:
- name: "my_jenkins_role"
permissions:
- "Overall/Administer"
assignments:
- "my_okta_group"
BTW, I specify the yaml with Helm:
...
JCasC:
configScripts:
okta: |
jenkins:
securityRealm:
...

Google OAuth 2.0 failing with Error 400: invalid_request for some client_id, but works well for others in the same project

We have some apps (or maybe we should call them a handful of scripts) that use Google APIs to facilitate some administrative tasks. Recently, after making another client_id in the same project, I started getting an error message similar to the one described in localhost redirect_uri does not work for Google Oauth2 (results in 400: invalid_request error). I.e.,
Error 400: invalid_request
You can't sign in to this app because it doesn't comply with Google's
OAuth 2.0 policy for keeping apps secure.
You can let the app developer know that this app doesn't comply with
one or more Google validation rules.
Request details:
The content in this section has been provided by the app developer.
This content has not been reviewed or verified by Google.
If you’re the app developer, make sure that these request details
comply with Google policies.
redirect_uri: urn:ietf:wg:oauth:2.0:oob
How do I get through this error? It is important to note that:
The OAuth consent screen for this project is marked as "Internal". Therefore any mentions of Google review of the project, or publishing status are irrelevant
I do have "Trust internal, domain-owned apps" enabled for the domain
Another client id in the same project works and there are no obvious differences between the client IDs - they are both "Desktop" type which only gives me a Client ID and Client secret that are different
This is a command line script, so I use the "copy/paste" verification method as documented here hence the urn:ietf:wg:oauth:2.0:oob redirect URI (copy/paste is the only friendly way to run this on a headless machine which has no browser).
I was able to reproduce the same problem in a dev domain. I have three client ids. The oldest one is from January 2021, another one from December 2021, and one I created today - March 2022. Of those, only the December 2021 works and lets me choose which account to authenticate with before it either accepts it or rejects it with "Error 403: org_internal" (this is expected). The other two give me an "Error 400: invalid_request" and do not even let me choose the "internal" account. Here are the URLs generated by my app (I use the ruby google client APIs) and the only difference between them is the client_id - January 2021, December 2021, March 2022.
Here is the part of the code around the authorization flow, and the URLs for the different client IDs are what was produced on the $stderr.puts url line. It is pretty much the same thing as documented in the official example here (version as of this writing).
OOB_URI = 'urn:ietf:wg:oauth:2.0:oob'
def user_credentials_for(scope, user_id = 'default')
token_store = Google::Auth::Stores::FileTokenStore.new(:file => token_store_path)
authorizer = Google::Auth::UserAuthorizer.new(client_id, scope, token_store)
credentials = authorizer.get_credentials(user_id)
if credentials.nil?
url = authorizer.get_authorization_url(base_url: OOB_URI)
$stderr.puts ""
$stderr.puts "-----------------------------------------------"
$stderr.puts "Requesting authorization for '#{user_id}'"
$stderr.puts "Open the following URL in your browser and authorize the application."
$stderr.puts url
code = $stdin.readline.chomp
$stderr.puts "-----------------------------------------------"
credentials = authorizer.get_and_store_credentials_from_code(
user_id: user_id, code: code, base_url: OOB_URI)
end
credentials
end
Please see https://stackoverflow.com/a/71491500/1213346 for a "proper" solution. This answer is just an ugly workaround that the community seems to like.
...
Here is a cringy workaround for this situation:
Replace urn:ietf:wg:oauth:2.0:oob with http://localhost:1/ in the code posted in the question. This makes the flow go through, my browser gets redirected and fails and I get an error messages like:
This site can’t be reached
The webpage at http://localhost:1/oauth2callback?
code=4/a3MU9MlhWxit8P7N8QsGtT0ye8GJygOeCa3MU9MlhWxit8P7N8QsGtT0y
e8GJygOeC&scope=email%20profile%20https... might be temporarily
down or it may have moved permanently to a new web address.
ERR_UNSAFE_PORT
Now copy the code code value from the failing URL, paste it into the app, and voila... same as before :)
P.S. Here is the updated "working" version:
def user_credentials_for(scope, user_id = 'default')
token_store = Google::Auth::Stores::FileTokenStore.new(:file => token_store_path)
authorizer = Google::Auth::UserAuthorizer.new(client_id, scope, token_store, "http://localhost:1/")
credentials = authorizer.get_credentials(user_id)
if credentials.nil?
url = authorizer.get_authorization_url
$stderr.puts ""
$stderr.puts "-----------------------------------------------"
$stderr.puts "Requesting authorization for '#{user_id}'"
$stderr.puts "Open the following URL in your browser and authorize the application."
$stderr.puts url
$stderr.puts
$stderr.puts "At the end the browser will fail to connect to http://localhost:1/?code=SOMECODE&scope=..."
$stderr.puts "Copy the value of SOMECODE from the address and paste it below"
code = $stdin.readline.chomp
$stderr.puts "-----------------------------------------------"
credentials = authorizer.get_and_store_credentials_from_code(
user_id: user_id, code: code)
end
credentials
end ```
I sent off an email to someone on the Google OAuth team. This is the gist of their response.
As I feared your issue is related to Making Google OAuth interactions safer by using more secure OAuth flows
The current recommendation from google is to move to use localhost/loopback redirects as recommended here: instructions-oob or use the OAuth for devices flow if you are using non-sensitive scopes and need a headless solution.
A solution for python.
As google_auth_oauthlib shows, InstalledAppFlow.run_console has been deprecated after Feb 28, 2022. And if you are using google-ads-python, you can just replace flow.run_console() by flow.run_local_server().
Let me post the "proper" solution as a separate answer, which is to actually follow the recommended procedure by implementing an HTTP listener in the ruby app. If this is running on an offline machine the listener will never get the code, but you can still paste the code from the failing URL.
require 'colorize'
require 'sinatra/base'
# A simplistic local server to receive authorization tokens from the browser
def run_local_server(authorizer, port, user_id)
require 'thin'
Thin::Logging.silent = true
Thread.new {
Thread.current[:server] = Sinatra.new do
enable :quiet
disable :logging
set :port, port
set :server, %w[ thin ]
get "/" do
request = Rack::Request.new env
state = {
code: request["code"],
error: request["error"],
scope: request["scope"]
}
raise Signet::AuthorizationError, ("Authorization error: %s" % [ state[:error] ] ) if state[:error]
raise Signet::AuthorizationError, "Authorization code missing from the request" if state[:code].nil?
credentials = authorizer.get_and_store_credentials_from_code(
user_id: user_id,
code: state[:code],
scope: state[:scope],
)
[
200,
{ "Content-Type" => "text/plain" },
"All seems to be OK. You can close this window and press ENTER in the application to proceed.",
]
end
end
Thread.current[:server].run!
}
end
# Returns user credentials for the given scope. Requests authorization
# if requrired.
def user_credentials_for(scope, user_id = 'default')
client_id = Google::Auth::ClientId.new(ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'])
token_store = Google::Auth::Stores::FileTokenStore.new(:file => ENV['GOOGLE_CREDENTIAL_STORE'])
port = 6969
redirect_uri = "http://localhost:#{port}/"
authorizer = Google::Auth::UserAuthorizer.new(client_id, scope, token_store, redirect_uri)
credentials = authorizer.get_credentials(user_id)
if credentials.nil? then
server_thread = run_local_server(authorizer, port, user_id)
url = authorizer.get_authorization_url
$stderr.puts ""
$stderr.puts "-----------------------------------------------"
$stderr.puts "Requesting authorization for '#{user_id.yellow}'"
$stderr.puts "Open the following URL in your browser and authorize the application."
$stderr.puts
$stderr.puts url.yellow.bold
$stderr.puts
$stderr.puts "⚠️ If you are authorizing on a different machine, you will have to port-forward"
$stderr.puts "so your browser can reach #{redirect_uri.yellow}"
$stderr.puts
$stderr.puts "⚠️ If you get a " << "This site can't be reached".red << " error in the browser,"
$stderr.puts "just copy the failing URL below. Copy the whole thing, starting with #{redirect_uri.yellow}."
$stderr.puts "-----------------------------------------------"
code = $stdin.readline.chomp
server_thread[:server].stop!
server_thread.join
credentials = authorizer.get_credentials(user_id)
# If the redirect failed, the user must have provided us with a code on their own
if credentials.nil? then
begin
require 'uri'
require 'cgi'
code = CGI.parse(URI.parse(code).query)['code'][0]
rescue StandardException
# Noop, if we could not get a code out of the URL, maybe it was
# not the URL but the actual code.
end
credentials = authorizer.get_and_store_credentials_from_code(
user_id: user_id,
code: code,
scope: scope,
)
end
end
credentials
end
credentials = user_credentials_for(['https://www.googleapis.com/auth/drive.readonly'])
In short, we run a web server expecting the redirect from the browser. It takes the code the browser sent, or it takes the code pasted by the user.
For headless Python scripts that need sensitive scopes, continuing to use run_console now produces the following (and the flow likely fails):
DeprecationWarning: New clients will be unable to use `InstalledAppFlow.run_console` starting on Feb 28, 2022. All clients will be unable to use this method starting on Oct 3, 2022. Use `InstalledAppFlow.run_local_server` instead. For details on the OOB flow deprecation, see https://developers.googleblog.com/2022/02/making-oauth-flows-safer.html?m=1#disallowed-oob
The official solution is to migrate to a flow that spins up a local server to handle the OAuth redirect, but this will not work on remote headless systems.
The solution Google adopted in gcloud is to run a local server on the same machine as the user's browser and then have the user copy the redirect URL requested from this local server back to the remote machine. Note that this requires having gcloud installed both on the remote machine and on the user's workstation.
As a hack for situations where installing a script to echo back the redirect URL on the workstation is not practical, we can use a redirect URL that is guaranteed to fail and just have the user copy back the URL of the error page on which they will land after authorization is complete.
import urllib
from google_auth_oauthlib.flow import InstalledAppFlow
def run_console_hack(flow):
flow.redirect_uri = 'http://localhost:1'
auth_url, _ = flow.authorization_url()
print(
"Visit the following URL:",
auth_url,
"After granting permissions, you will be redirected to an error page",
"Copy the URL of that error page (http://localhost:1/?state=...)",
sep="\n"
)
redir_url = input("URL: ")
query = urllib.parse.urlparse(redir_url).query
code = urllib.parse.parse_qs(query)['code'][0]
flow.fetch_token(code=code)
return flow.credentials
scopes = ['https://www.googleapis.com/auth/drive.file']
flow = InstalledAppFlow.from_client_secrets_file(secrets_file, scopes)
credentials = run_console_hack(flow)
We could also ask the user to pass back the code query string parameter directly but that is likely to be confusing and error-prone.
The use of 1 as the port number means that the request is guaranteed to fail, rather than potentially hit some service that happens to be running on that port. (e.g. Chrome will fail with ERR_UNSAFE_PORT without even trying to connect)
"Hello world" for this error:
Generating an authentication URL
https://github.com/googleapis/google-api-nodejs-client#generating-an-authentication-url
const {google} = require('googleapis');
const oauth2Client = new google.auth.OAuth2(
YOUR_CLIENT_ID,
YOUR_CLIENT_SECRET,
YOUR_REDIRECT_URL
);
// generate a url that asks permissions for Blogger and Google Calendar scopes
const scopes = [
'https://www.googleapis.com/auth/blogger',
'https://www.googleapis.com/auth/calendar'
];
const url = oauth2Client.generateAuthUrl({
// 'online' (default) or 'offline' (gets refresh_token)
access_type: 'offline',
// If you only need one scope you can pass it as a string
scope: scopes
});
If something goes wrong the first step is to Re Check again the three values of the google.auth.OAuth2 function.
1 of 2
Compare to the store values under Google APIs console:
YOUR_CLIENT_ID
YOUR_CLIENT_SECRET
YOUR_REDIRECT_URL -
For example http://localhost:3000/login
2 of 2 (environment variables)
A lot of times the values store inside .env. So re-check the env and the output under your files - for example index.ts (Even use console.log).
.env
# Google Sign-In (OAuth)
G_CLIENT_ID=some_id_1234
G_CLIENT_SECRET=some_secret_1234
PUBLIC_URL=http://localhost:3000
index
const auth = new google.auth.OAuth2(
process.env.G_CLIENT_ID,
process.env.G_CLIENT_SECRET,
`${process.env.PUBLIC_URL}/login`
);
SUM:
Something like this will not work
const oauth2Client = new google.auth.OAuth2(
"no_such_id",
"no_such_secret",
"http://localhost:3000/i_forgot_to_Authorised_this_url"
);
I've fixed this problem with recreate my App in google console. And I think the problem was with redirect_url. I had this problem when I was using 'Android' type of App in google console (in this case you can't configure redirect url). In my android App I'm using google auth with WebView so the best option here use use 'Web' type for your app in google console.
In my case, had to update plugins. by running following command-
bundle exec fastlane update_plugins
With this redirect uri was getting created properly as
https://accounts.google.com/o/oauth2/auth?access_type=offline&approval_prompt=force&client_id=563584335869-fgrhgmd47bqnekij5i8b5pr03ho849e6.apps.googleusercontent.com&include_granted_scopes=true&redirect_uri=http://localhost:8081&response_type=code&scope=https://www.googleapis.com/auth/cloud-platform&state=2ce8a59b2d403f3a89fa635402bfc5c4
steps.oauth.v2.invalid_request 400 This error name is used for multiple different kinds of errors, typically for missing or incorrect parameters sent in the request. If is set to false, use fault variables (described below) to retrieve details about the error, such as the fault name and cause.
GenerateAccessToken GenerateAuthorizationCode
GenerateAccessTokenImplicitGrant
RefreshAccessToken
Google Oauth Policy

Problem with scope results during token generation when apim3.2.0 is configured with is5.10.0 as keymanager

I found strange behavior during checking functionality of WSO2 AM + IS as key manager.
Env I:
WSO2 AM 3.2.0 (GA pack)
Env II:
WSO2 AM 3.2.0 (GA pack)
WSO2 IS 5.10.0 (GA pack)
Configuration:
created users: user-low, user-high
created roles: low, high
user-low has assigned low role
user-high has assigned high role
created shared scopes: high-scope, low-scope
high-scope pointed to high role
low-scope pointed to low role
published api with 2 endpoint
/unsecure (has pointed scope: low-scope)
/secured (has pointed scope: high-scope)
Behaviour on ENV I (working fine - expected behaviour):
After trying generate token by "user-high" with scopes high-scope, low-scope as a receive on endpoint: https://localhost:9443/generate-token
response:
{
"accessToken":"eyJ4NXQiOiJNell4TW1Ga09HWXdNV0kwWldObU5EY3hOR1l3WW1NNFpUQTNNV0kyTkRBelpHUXpOR00wWkdSbE5qSmtPREZrWkRSaU9URmtNV0ZoTXpVMlpHVmxOZyIsImtpZCI6Ik16WXhNbUZrT0dZd01XSTBaV05tTkRjeE5HWXdZbU00WlRBM01XSTJOREF6WkdRek5HTTBaR1JsTmpKa09ERmtaRFJpT1RGa01XRmhNelUyWkdWbE5nX1JTMjU2IiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJ1c2VyLWhpZ2hAY2FyYm9uLnN1cGVyIiwiYXV0IjoiQVBQTElDQVRJT04iLCJhdWQiOiIxeXZfaUZ2d3RvZmZ4RU90ZlJGZmpseGNjNG9hIiwibmJmIjoxNjEyMzU3NTExLCJhenAiOiIxeXZfaUZ2d3RvZmZ4RU90ZlJGZmpseGNjNG9hIiwic2NvcGUiOiJISUdIIGFtX2FwcGxpY2F0aW9uX3Njb3BlIiwiaXNzIjoiaHR0cHM6XC9cL2xvY2FsaG9zdDo5NDQzXC9vYXV0aDJcL3Rva2VuIiwiZXhwIjoxNjEyMzYxMTExLCJpYXQiOjE2MTIzNTc1MTEsImp0aSI6ImQ3OTQ3ODc0LTRlNTMtNGI1My1iMWUwLTE0NGEwYTY2MDU5ZSJ9.KBol5clfIxScVPYVzdmBkz0APaE7uL8genldz8tx_G0FnaJStjn0tizDQfdcc46ZaEC1ahEfsGqBea6sJ8dpFucpf3ZqxnCz7CoJnVLU5F4lAXm_C3imWhOWLxF_wka0dlGExPDBpXQOmnspe2b45DSpIpz3zbTnnuClFM91tJkWrG9-k_ZIUHikI34m3aWltotXJzQJojdhL42pUCCttGcNGDbU9vfZ4wOcRx4fiVe6z0azvDBGP3FBSY00HyBFUo7ME9dqMaU_EDTybk77uLHyNGoQggOO42WU0ZfanrGlsYJSuzyQi4VVW3V1Uy6591b18LA28zq1c9Ay2-aMXw",
"tokenScopes":[
"HIGH",
"am_application_scope"
],
"validityTime":3600
}
Behaviour on ENV II:
After trying generate token by "user-high" with scopes high-scope, low-scope as a receive on endpoint: https://localhost:9443/generate-token
response:
{
"accessToken":"eyJ4NXQiOiJNell4TW1Ga09HWXdNV0kwWldObU5EY3hOR1l3WW1NNFpUQTNNV0kyTkRBelpHUXpOR00wWkdSbE5qSmtPREZrWkRSaU9URmtNV0ZoTXpVMlpHVmxOZyIsImtpZCI6Ik16WXhNbUZrT0dZd01XSTBaV05tTkRjeE5HWXdZbU00WlRBM01XSTJOREF6WkdRek5HTTBaR1JsTmpKa09ERmtaRFJpT1RGa01XRmhNelUyWkdWbE5nX1JTMjU2IiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJ1c2VyLWhpZ2hAY2FyYm9uLnN1cGVyIiwiYXV0IjoiQVBQTElDQVRJT04iLCJhdWQiOiJHU1RYeVdhNlNCeHRNRWhiVF91SnA4ZmtBNUlhIiwibmJmIjoxNjEyMzU3MDQ5LCJhenAiOiJHU1RYeVdhNlNCeHRNRWhiVF91SnA4ZmtBNUlhIiwic2NvcGUiOiJISUdIIExPVyIsImlzcyI6Imh0dHBzOlwvXC9sb2NhbGhvc3Q6OTQ0M1wvb2F1dGgyXC90b2tlbiIsImV4cCI6MTYxMjM2MDY0OSwiaWF0IjoxNjEyMzU3MDQ5LCJqdGkiOiI0OTdhOTQ3OC0zZjg3LTQ0NmMtYjQ4OS1kMjI1MmQ2NDA2ZTYifQ.AoFKYLRsZBHELh8m0XBV0ndr19SMl0xMwzACG5-Q_ek7VRtWmocqJeAEjrXguUhcIUqHs843NPzcf185BpEjwCwJcXcR7ssIqxzINYSH0s7_LTm4X7XHMxV4cnF8gAiRnUQhGZQHgCyWI6NJ5VAcpIde5BnWaVcmx2Q5VFOFXGskfOB7325LcmhMS13Ni5oK2vb7YcTs059Zhoj13MrRnAOKoE1xrO9ioSVBXj9oX5RZ2uvdT_V3FQNWklc5jdMgebHDUQw-q_C5q9qhlGRZKql2ktcJ3OUeyGnJYEppuM0tOKCSTeH93MNcf6TAXYWEiRioa0FhRJblrfCIQrsHvA",
"tokenScopes":[
"HIGH",
"LOW"
],
"validityTime":3600
}
It's looks like in ENV II generating tokens not working properly. There is my questions:
Is it a bug of that versions or some misconfiguration issues?
If it's bug where i should start to try fix that situation, someone could point me where is implemented that functionality? (I want use opensource version over update products using WUM)
Is that situation was resolved in later versions of submodules, which are used to build product-api, and product-is?
Thanks in advance for any help and suggestions!

How do I get the JSON response from Dialogflow with Rails?

I understand the whole process of dialogflow and I have a working deployed bot with 2 different intents. How do I actually get the response from the bot when a user answers questions? (I set the bot on fulfillment to go to my domain). Using rails 5 app and it's deployed with Heroku.
Thanks!
If you have already set the GOOGLE_APPLICATION_CREDENTIALS path to the jso file, now you can test using a ruby script.
Create a ruby file -> ex: chatbot.rb
Write the code bellow in the file.
project_id = "Your Google Cloud project ID"
session_id = "mysession"
texts = ["hello"]
language_code = "en-US"
require "google/cloud/dialogflow"
session_client = Google::Cloud::Dialogflow::Sessions.new
session = session_client.class.session_path project_id, session_id
puts "Session path: #{session}"
texts.each do |text|
query_input = { text: { text: text, language_code: language_code } }
response = session_client.detect_intent session, query_input
query_result = response.query_result
puts "Query text: #{query_result.query_text}"
puts "Intent detected: #{query_result.intent.display_name}"
puts "Intent confidence: #{query_result.intent_detection_confidence}"
puts "Fulfillment text: #{query_result.fulfillment_text}\n"
end
Insert your project_id. You can find this information on your agent on Dialogflow. Click on the gear on the right side of the Agent's name in the left menu.
Run the ruby file in the terminal or in whatever you using to run ruby files. Then you see the bot replying to the "hello" message you have sent.
Obs: Do not forget to install the google-cloud gem:
Not Entirely familiar with Dilogflow, but if you want to receive a response when an action occurs on another app this usually mean you need to receive web-hooks from them
A WebHook is an HTTP callback: an HTTP POST that occurs when something happens; a simple event-notification via HTTP POST. A web application implementing WebHooks will POST a message to a URL when certain things happen.
I would recommend checking their fulfillment documentation for an example. Hope this helps you out.

Trouble authenticating with Google Content API for Shopping

I'm trying to use OAuth2 for Server to Server Applications in conjunction with Google's Content API for Shopping using the google-api-client gem and Ruby on Rails 3.2.5. Also, I have already set up my merchant account as prescribed in the Content API documentation.
This was the best way I found to be able to:
create/update products in the background
have created products fall under my company's Google Products 'umbrella'
not require every user to authenticate/authorize when their token expires
Using lines 1 - 23 from this sample as a starting point, I've begun to write the following module for use in background jobs:
require 'httparty'
require 'google/api_client'
module GoogleProducts
GOOGLE_CONFIG = YAML.load_file(File.join(Rails.root, "config", "google.yml"))[Rails.env]
CLIENT_ID = "XXXXXXXXXXXX#developer.gserviceaccount.com"
MERCHANT_ID = "XXXXXXX"
SCOPE = "https://www.googleapis.com/auth/structuredcontent"
KEY_FILE_PATH = File.join(Rails.root, "config", "my-privatekey.p12")
KEY_FILE_PASS = "XXXXXXXXXX"
def self.add_item(item_id)
self.fetch_token
xml = self.gen_item_xml(item_id)
headers = {"Content-type" => "application/atom+xml", "Content-Length" => xml.length.to_s}
url = "https://content.googleapis.com/content/v1/#{MERCHANT_ID}/items/products/generic?access_token=#{$gp_token}"
response = HTTParty.post(url, :body => xml, :headers => headers).parsed_response
end
def self.gen_item_xml(item_id)
#building product xml
end
private
def self.fetch_token
api_client = Google::APIClient.new(:authorization => :oauth2)
key = Google::APIClient::PKCS12.load_key(KEY_FILE_PATH, KEY_FILE_PASS)
asserter = Google::APIClient::JWTAsserter.new(CLIENT_ID, SCOPE, key)
begin
api_client.authorization = asserter.authorize
#todo - store in something other than a global
$gp_token = api_client.authorization.access_token
rescue Signet::AuthorizationError => e
puts e.message
ensure
return $gp_token
end
end
end
Everything seemingly works fine - the authentication, the handling of the auth token - until I attempt to actually add an item, which I get the following when I do:
<errors xmlns='http://schemas.google.com/g/2005'>
<error>
<domain>GData</domain>
<code>ServiceForbiddenException</code>
<internalReason>Could not find authenticated customer</internalReason>
</error>
</errors>
Any ideas?
After much anguish and mental toil, I've finally solved my issue!
Since I am using OAuth 2 Server to Server authentication the suggestion hjblok gave didn't apply (thanks for giving it a shot, though!).
I simply added the email address that was associated with my Service Account key from the Google API Console (e.g. XXXXXXXXXXXX#developer.gserviceaccount.com) to my Google Merchant account (Settings > Users on the merchant admin page), and it worked.
If there's any clarification needed, please feel free to comment!
The Google Content API documentation says you need to set it up in the Settings page of the Google Merchant Center:
https://developers.google.com/shopping-content/getting-started/usingapi-products
EDIT rewrote the answer after diving into the Google's API documentation
Did you already try to use Google's OAuth 2.0 playground? I was able to successfully access https://content.googleapis.com/content/v1/#{MERCHANT_ID}/items/products/generic.
In "Step 1" I've chosen the "Content API for Shopping" and then authorized the API with my account.
Then in "Step 2" I've "exchanged authorization code for tokens", which results in a "refresh token" and an "access token".
Then in "Step 3" I've invoked a GET request to https://content.googleapis.com/content/v1/1234567/items/products/generic. Because 1234567 is not a valid MERCHANT_ID it returns an Error. But the Error Messages contains a MERCHANT_ID which actually belongs to your account.
I repeated "Step 3" but now with the correct MERCHANT_ID. Which returns a HTTP/1.1 200 OK with the requested items in the body.
Furthermore I'm not sure, but doesn't Google API expect an Authorization header to be present with the access_token ($gp_token)? Within the OAuth 2.0 playground this Authorization header is used to sent the access_token.
I also found the Structured Content API demo page (https://google-content-api-tools.appspot.com/demo/demo.html), which is more specific to the Content API for Shopping.

Resources