403 IAM permission 'dialogflow.sessions.detectIntent' error in flask - python

I am trying to use Dialogflow in my Flask app but I am getting 403 IAM permission 'dialogflow.sessions.detectIntent' I have checked twice my role is project owner in cloud console. I am following this tutorial.
This is my app.py
from flask import Flask
from flask import request, jsonify
import os
import dialogflow
from google.api_core.exceptions import InvalidArgument
import requests
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = 'service_key.json'
DIALOGFLOW_PROJECT_ID = 'whatsappbotagent-gmsl'
DIALOGFLOW_LANGUAGE_CODE = 'en'
SESSION_ID = 'me'
app = Flask(__name__)
app.config["DEBUG"] = True
#app.route('/')
def root():
return "Hello World"
#app.route('/api/getMessage', methods = ['POST'])
def home():
message = request.form.get('Body')
mobnu = request.form.get('From')
session_client = dialogflow.SessionsClient()
session = session_client.session_path(DIALOGFLOW_PROJECT_ID, SESSION_ID)
text_input = dialogflow.types.TextInput(text = message, language_code = DIALOGFLOW_LANGUAGE_CODE)
query_input = dialogflow.types.QueryInput(text = text_input)
try:
response = session_client.detect_intent(session = session, query_input = query_input)
except InvalidArgument:
raise
print("Query text: ", response.query_result.query_text)
# sendMessage()
return response.query_result.fullfilment_text
if __name__ == '__main__':
app.run()

OK, so after a lot of searching and trial and error I found the issue.
Actually while creating a service account the user gets a service email and that email will have access to use the project but I was not adding that email in project permission emails. Adding that email in IAM & Admin > IAM > +ADD will make it work.
Maybe there was a better solution but this is what works for me and hopefully solves others problem if have similar issue like this.

Related

Dialogflow response from webhook using flask python integration platform digital human uneeq

I am trying to send a response to Dialogflow Es using webhook written in python-flask.
Integration platform :
digital human --> Uneeq
I tried with the fulfillment library (dialogflow_fulfillment) but it was not working so I tried it without a library like :
if req.get('queryResult').get('action') == 'appointment':
print("ïntent triggered")
return {"fulfillmentText": 'BUDDY!'}
It worked!
Now the issue is before integrating this chatbot to digital human I wrote the whole code using the fulfillment library. Now I need to change the whole code for this purpose.
As I am unable to extract entities using context-based intents (session-vars) like :
case = agent.context.get('session-vars').get('parameters').get('case')
name = agent.context.get('session-vars').get('parameters').get('name')['name']
email = agent.context.get('session-vars').get('parameters').get('email')
phone = agent.context.get('session-vars').get('parameters').get('phone')
So my question is how to accomplish this task successfully?
If I try to use the fulfillment library then how to send a response to Dialogflow so that digital human will understand it since "agent.add" doesn't work.
Or, If I try to do it without a library then how to extract entities(from the session-vars) like (case, name, email, phone).
I need to save all these parameters(entities) in firebase, These output context (only session-vars parameters not other contexts):
This is the response I am getting correctly without library:
Unable to proceed at any cost!
looking forward to your response.
Thanks in advance!
Complete code (with the library):
from dialogflow_fulfillment import WebhookClient
from flask import Flask, request, Response
import json
app = Flask(__name__)
def handler(agent: WebhookClient) :
"""Handle the webhook request.."""
req = request.get_json(force=True)
a= req.get('queryResult').get('action')
if req.get('queryResult').get('action') == 'appointment':
name = agent.context.get('session-vars').get('parameters').get('name')['name']
email = agent.context.get('session-vars').get('parameters').get('email')
phone = agent.context.get('session-vars').get('parameters').get('phone')
print("ïntent triggered")
agent.add('BUDDY!')
#app.route('/webhook', methods=['GET', 'POST'])
def webhook():
req = request.get_json(force=True)
agent = WebhookClient(req)
agent.handle_request(handler)
return agent.response
if __name__ == '__main__':
app.run(debug=True)
Complete code (without library):
import urllib
import json
import os
from flask import Flask
from flask import request
from flask import make_response
# Flask app should start in global layout
app = Flask(__name__)
#app.route('/webhook', methods=['POST'])
def webhook():
req = request.get_json(silent=True, force=True)
res = makeWebhookResult(req)
res = json.dumps(res, indent=4)
r = make_response(res)
#print(res)
r.headers['Content-Type'] = 'application/json'
return r
def makeWebhookResult(req):
intent_name = req.get('queryResult').get('intent').get('displayName')
print(intent_name)
action_name = req['queryResult']['action']
if req.get('queryResult').get('action') == 'input.welcome':
print("intent trigered")
return {"fulfillmentText": 'Hi there'}
if action_name == 'test':
cont = req.get('queryResult').get('outputContexts')[0]['name']
print(type(cont))
x = cont.split("/")
print(x[6])
if x[6] == 'session-vars' :
para = req['queryResult']['outputContexts']
print(para)
print("test intent trigered")
return {"fulfillmentText": 'Bye there'}
if __name__ == '__main__':
app.run(debug=True)

Heroku app having trouble with Spotify API token

So I've been using Flask to create an app that uses Spotify API, and the authorization code flow works perfectly fine when running on my localhost server. However, after deploying the app to Heroku, occasionally the app will crash and give me a 'token not provided' error in the logs. Sometimes it'll work flawlessly, but when I play around with it more it'll crash. I try to redo the process on my localhost but it doesn't seem to break. I'm storing the token in sessions, could it be that Heroku is having problem with retrieving session variables?
Here's the flask app code
#Home view
#app.route('/')
#app.route('/home')
def home():
return render_template('home.html', title='Home')
#Login view
#app.route('/login', methods=['GET','POST'])
def login():
AUTH_FIRST = req_auth()
return redirect(AUTH_FIRST)
#Callback view for Spotify API
#app.route('/callback')
def callback():
if request.args.get('code'):
code = request.args.get('code')
token = req_token(code)
session['token'] = token
return redirect(url_for('generate_playlist'))
else:
return redirect(url_for('home'))
#Generate playlist view
#app.route('/generate_playlist', methods=['GET', 'POST'])
def generate_playlist():
if request.method == 'POST':
levels = int(float(request.form['level']))
token = session.get('token')
generate(token, levels)
return redirect(url_for('success'))
else:
return redirect(url_for('generate_playlist'))
This is the backend authorization code (I've commented out some parts to make it simpler, yet it still doesn't work):
client_id = os.environ.get("CLIENT_ID")
client_secret = os.environ.get("CLIENT_SECRET")
redirect_uri = os.environ.get('REDIRECT_URI')
state = ''.join(random.choice(string.ascii_lowercase + string.digits) for n in range(8))
scope = 'user-top-read user-library-read playlist-modify-public'
def req_auth():
show_dialog = "false"
AUTH_FIRST_URL = f'https://accounts.spotify.com/authorize?client_id={client_id}&response_type=code&redirect_uri={quote("https://surprisify-playlist-generator.herokuapp.com/callback")}&show_dialog={show_dialog}&state={state}&scope={scope}'
return AUTH_FIRST_URL
def req_token(code):
#B64 encode variables
client_creds = f"{client_id}:{client_secret}"
client_creds_b64 = base64.b64encode(client_creds.encode())
#Token data
token_data = {
"grant_type": "authorization_code",
"code": code,
"redirect_uri": "https://surprisify-playlist-generator.herokuapp.com/callback"
}
#Token header
token_header = {
"Authorization": f"Basic {client_creds_b64.decode()}"
}
#Make request post for token info
token_json = requests.post('https://accounts.spotify.com/api/token', data=token_data, headers=token_header)
return token_json.json()['access_token']
#Checking if token is still valid, otherwise, refresh
# if token_json.json()['expires_in']:
# now = datetime.datetime.now()
# expires_in = token_json.json()['expires_in']
# expires = now + datetime.timedelta(seconds=expires_in)
# if now > expires:
# refresh_token_data = {
# "grant_type": "refresh_token",
# "refresh_token": token_json.json()['refresh_token']
# }
# refresh_token_json = requests.post('https://accounts.spotify.com/api/token', data=refresh_token_data, headers=token_header)
# token = refresh_token_json.json()['access_token']
# return token
# else:
# token = token_json.json()['access_token']
# return token
# else:
# token = token_json.json()['access_token']
# return token
Could the error happen after you restart the browser? Flask session cookies are stored (by default) until you close the browser. Then, if you don't authenticate again, calling session.get(token) will return None and is likely why you get the error. You could try checking if token is None in generate_playlist() and requiring re-authentication with a redirect.
Here is an implementation of the Spotify authorization code flow using Spotipy that I have used with success in my own project: https://stackoverflow.com/a/57929497/6538328 (method 2).

MismatchingStateError: mismatching_state: CSRF Warning! State not equal in request and response

This is driving me absolutely crazy and preventing me from being able to do local dev/test.
I have a flask app that uses authlib (client capabilities only). When a user hits my home page, my flask backend redirects them to /login which in turn redirects to Google Auth. Google Auth then posts them back to my app's /auth endpoint.
For months, I have been experiencing ad-hoc issues with authlib.integrations.base_client.errors.MismatchingStateError: mismatching_state: CSRF Warning! State not equal in request and response. It feels like a cookie problem and most of the time, I just open a new browser window or incognito or try to clear cache and eventually, it sort of works.
However, I am now running the exact same application inside of a docker container and at one stage this was working. I have no idea what I have changed but whenever I browse to localhost/ or 127.0.0.1/ and go through the auth process (clearing cookies each time to ensure i'm not auto-logged in), I am constantly redirected back to localhost/auth?state=blah blah blah and I experience this issue:
authlib.integrations.base_client.errors.MismatchingStateError: mismatching_state: CSRF Warning! State not equal in request and response.
I think the relevant part of my code is:
#app.route("/", defaults={"path": ""})
#app.route("/<path:path>")
def catch_all(path: str) -> Union[flask.Response, werkzeug.Response]:
if flask.session.get("user"):
return app.send_static_file("index.html")
return flask.redirect("/login")
#app.route("/auth")
def auth() -> Union[Tuple[str, int], werkzeug.Response]:
token = oauth.google.authorize_access_token()
user = oauth.google.parse_id_token(token)
flask.session["user"] = user
return flask.redirect("/")
#app.route("/login")
def login() -> werkzeug.Response:
return oauth.google.authorize_redirect(flask.url_for("auth", _external=True))
I would hugely appreciate any help.
When I run locally, I start with:
export FLASK_APP=foo && flask run
When I run inside docker container, i start with:
.venv/bin/gunicorn -b :8080 --workers 16 foo
Issue was that SECRET_KEY was being populated using os.random which yielded different values for different workers and thus, couldn't access the session cookie.
#adamcunnington here is how you can debug it:
#app.route("/auth")
def auth() -> Union[Tuple[str, int], werkzeug.Response]:
# Check these two values
print(flask.request.args.get('state'), flask.session.get('_google_authlib_state_'))
token = oauth.google.authorize_access_token()
user = oauth.google.parse_id_token(token)
flask.session["user"] = user
return flask.redirect("/")
Check the values in request.args and session to see what's going on.
Maybe it is because Flask session not persistent across requests in Flask app with Gunicorn on Heroku
How I Fix My Issue
install old version of authlib it work fine with fastapi and flask
Authlib==0.14.3
For Fastapi
uvicorn==0.11.8
starlette==0.13.6
Authlib==0.14.3
fastapi==0.61.1
Imporantt if using local host for Google auth make sure get https certifcate
install chocolatey and setup https check this tutorial
https://dev.to/rajshirolkar/fastapi-over-https-for-development-on-windows-2p7d
ssl_keyfile="./localhost+2-key.pem" ,
ssl_certfile= "./localhost+2.pem"
--- My Code ---
from typing import Optional
from fastapi import FastAPI, Depends, HTTPException
from fastapi.openapi.docs import get_swagger_ui_html
from fastapi.openapi.utils import get_openapi
from starlette.config import Config
from starlette.requests import Request
from starlette.middleware.sessions import SessionMiddleware
from starlette.responses import HTMLResponse, JSONResponse, RedirectResponse
from authlib.integrations.starlette_client import OAuth
# Initialize FastAPI
app = FastAPI(docs_url=None, redoc_url=None)
app.add_middleware(SessionMiddleware, secret_key='!secret')
#app.get('/')
async def home(request: Request):
# Try to get the user
user = request.session.get('user')
if user is not None:
email = user['email']
html = (
f'<pre>Email: {email}</pre><br>'
'documentation<br>'
'logout'
)
return HTMLResponse(html)
# Show the login link
return HTMLResponse('login')
# --- Google OAuth ---
# Initialize our OAuth instance from the client ID and client secret specified in our .env file
config = Config('.env')
oauth = OAuth(config)
CONF_URL = 'https://accounts.google.com/.well-known/openid-configuration'
oauth.register(
name='google',
server_metadata_url=CONF_URL,
client_kwargs={
'scope': 'openid email profile'
}
)
#app.get('/login', tags=['authentication']) # Tag it as "authentication" for our docs
async def login(request: Request):
# Redirect Google OAuth back to our application
redirect_uri = request.url_for('auth')
print(redirect_uri)
return await oauth.google.authorize_redirect(request, redirect_uri)
#app.route('/auth/google')
async def auth(request: Request):
# Perform Google OAuth
token = await oauth.google.authorize_access_token(request)
user = await oauth.google.parse_id_token(request, token)
# Save the user
request.session['user'] = dict(user)
return RedirectResponse(url='/')
#app.get('/logout', tags=['authentication']) # Tag it as "authentication" for our docs
async def logout(request: Request):
# Remove the user
request.session.pop('user', None)
return RedirectResponse(url='/')
# --- Dependencies ---
# Try to get the logged in user
async def get_user(request: Request) -> Optional[dict]:
user = request.session.get('user')
if user is not None:
return user
else:
raise HTTPException(status_code=403, detail='Could not validate credentials.')
return None
# --- Documentation ---
#app.route('/openapi.json')
async def get_open_api_endpoint(request: Request, user: Optional[dict] = Depends(get_user)): # This dependency protects our endpoint!
response = JSONResponse(get_openapi(title='FastAPI', version=1, routes=app.routes))
return response
#app.get('/docs', tags=['documentation']) # Tag it as "documentation" for our docs
async def get_documentation(request: Request, user: Optional[dict] = Depends(get_user)): # This dependency protects our endpoint!
response = get_swagger_ui_html(openapi_url='/openapi.json', title='Documentation')
return response
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, port=8000,
log_level='debug',
ssl_keyfile="./localhost+2-key.pem" ,
ssl_certfile= "./localhost+2.pem"
)
.env file
GOOGLE_CLIENT_ID=""
GOOGLE_CLIENT_SECRET=""
Google Console Setup

how get jwt token from IAP in Flask?

I have a simple Flask application on a Google App Engine, protected by Identity-Aware Proxy.
The authentication works well but I only recover the GCP_IAP_UID cookie, when I want to recover the JWT found in GCP_IAAP_AUTH_TOKEN_XXXXX.
I have tried
google.auht jwt
Flask request cookies
Requests
None of this module retrieve the token.
The browser shows the cookies I need (show image linked below), but Flask can't catch them.
Any idea is welcome
I try with the google.auth jwt, but it's empty
I try with Flask request.cookies but I get only on cookie, the UID (see code)
I try with requests.cookies.RequestsCookieJar (last try) but there is no cookie
My apps run with python 37 and here are the requirements:
Flask==1.1.2
Flask-SSLify==0.1.5
Werkzeug==1.0.1
google-api-python-client==1.6.0
google-cloud-storage==1.6.0
gunicorn==19.10.0
oauth2client==4.1.3
six==1.14.0
requests_toolbelt==0.9.1
google-auth-httplib2==0.0.3
ez-setup==0.9
Below the code of the init.py where I want to validate the jwt.
import logging
from flask import Flask, redirect, url_for, request
from google.auth import jwt
import requests
user_email = ""
nickname = ""
jwtr = ""
try:
import googleclouddebugger
googleclouddebugger.enable()
except ImportError:
pass
def create_app(config, debug=False, testing=True, config_overrides=None):
app = Flask(__name__)
app.config.from_object(config)
app.debug = debug
app.testing = testing
if config_overrides:
app.config.update(config_overrides)
# Configure logging
# if not app.testing:
logging.basicConfig(level=logging.INFO)
# Register the Bookshelf CRUD blueprint.
from .crud import crud
app.register_blueprint(crud, url_prefix='/app')
# Add a default root route.
#app.route("/")
def index():
jwtr = ""
# Goto see the log below
logging.info("1 nb cookies={}".format(len(request.cookies)))
logging.info("GCP_IAP_UID={}".format(request.cookies.get('GCP_IAP_UID')))
jar = requests.cookies.RequestsCookieJar()
logging.info("2 nb cookies={}".format(len(jar)))
for cle in jar.keys():
if cle.startswith('GCP_IAAP_AUTH_TOKEN_'):
jwtr = jar.get(cle)
logging.info("jwtr={}".format(jwtr))
try:
user_id, user_email, error_str = validate_iap_jwt_from_app_engine(jwtr,
'123456789012', 'xxxxx-yyyy')
if user_email is not None:
nickname = user_email.split('#')[0]
logging.info("nickmane="+nickname + " user_id="+user_id + " user_email=" +
user_email)
return redirect(url_for('crud.index'))
else:
return ""
except (ValueError, requests.exceptions.RequestException) as e:
logging.error("C'est moche !!{}!!".format(e))
return ""
Last but least a log file:
INFO:root:1 nb cookies=1
INFO:root:GCP_IAP_UID=10944565464656564
INFO:root:2 nb cookies=0
ERROR:root:**ERROR: JWT validation error Wrong number of segments in token: b''**
Cookies at browser level
in fact the jwt token can be found in the header like this:
AUTHORIZATION_HEADER = 'X-Goog-Iap-Jwt-Assertion'
if request.headers.get(AUTHORIZATION_HEADER):
jwtr = request.headers.get(AUTHORIZATION_HEADER)
logging.info("header authz={}".format(jwtr))
You can found all you need in https://cloud.google.com/iap/docs/signed-headers-howto

Flask Authentication with LDAP using flask_ldap3_login

I am trying to authenticate my Flask app with the Active Directory using flask_ldap3_login. I have written the code to check the connection with active directory:
from flask_ldap3_login import LDAP3LoginManager
config = dict()
config['LDAP_HOST'] = 'my_ldap_host'
config['LDAP_BASE_DN'] = 'dc=internal,dc=com'
config['LDAP_USER_DN'] = 'ou=users'
config['LDAP_GROUP_DN'] = 'ou=groups'
config['LDAP_USER_RDN_ATTR'] = 'cn'
config['LDAP_USER_LOGIN_ATTR'] = 'dn'
config['LDAP_BIND_USER_DN'] = None
config['LDAP_BIND_USER_PASSWORD'] = None
ldap_manager = LDAP3LoginManager()
ldap_manager.init_config(config)
response = ldap_manager.authenticate('username', 'password')
print response.status
When I provide with my LDAP credentials it throws the error
raise LDAPOperationResult(result=result['result'], description=result['description'], dn=result['dn'], message=result['message'], response_type=result['type'])
ldap3.core.exceptions.LDAPOperationsErrorResult: LDAPOperationsErrorResult - 1 - operationsError - None - 000004DC: LdapErr: DSID-0C0906E8, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, v1db1 - searchResDone - None
Can someone tell me the proper way to authenticate the flask application with LDAP?
You need to provide login and password to access your LDAP:
app.config['LDAP_BIND_USER_DN'] = 'CN=someUser,CN=Users,DC=domain,DC=local'
app.config['LDAP_BIND_USER_PASSWORD'] = "secretpw"
Did you get it to work?
Did you have these lines in your code? If you didn't, why?
login_manager = LoginManager(app) # Flask-Login Manager
ldap_manager = LDAP3LoginManager(app) # Setup a LDAP3 Login Manager

Categories

Resources