I am writing a script that uses Google's authenticated login. I am currently getting an access token and the user's email address and passing it to my function that connects to gmail using imap and then does some stuff with the emails. I'm generating the auth string like i've seen others do online however I am receiving this error:
Traceback (most recent call last):
File "/Library/Python/2.7/site-packages/flask/app.py", line 2000, in __call__
return self.wsgi_app(environ, start_response)
File "/Library/Python/2.7/site-packages/flask/app.py", line 1991, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "/Library/Python/2.7/site-packages/flask/app.py", line 1567, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Library/Python/2.7/site-packages/flask/app.py", line 1988, in wsgi_app
response = self.full_dispatch_request()
File "/Library/Python/2.7/site-packages/flask/app.py", line 1641, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Library/Python/2.7/site-packages/flask/app.py", line 1544, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/Library/Python/2.7/site-packages/flask/app.py", line 1639, in full_dispatch_request
rv = self.dispatch_request()
File "/Library/Python/2.7/site-packages/flask/app.py", line 1625, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/Users/Harrison/Desktop/Uber/UberStats.py", line 60, in index
return Uber_Cost(email_address, access_token)
File "/Users/Harrison/Desktop/Uber/UberStats.py", line 103, in Uber_Cost
mail.authenticate('XOAUTH2', lambda x: auth_string)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/imaplib.py", line 364, in authenticate
raise self.error(dat[-1])
error: [AUTHENTICATIONFAILED] Invalid credentials (Failure)
I am printing out the access code as well as the email address that i'm logging in with, so I know those values aren't null. Am I generating the auth string wrong? Am I not authenticating with imap properly?
Here's my code:
from flask import Flask, request, url_for, session, redirect, jsonify
from flask_oauth import OAuth
import json
import imaplib
import email
from bs4 import BeautifulSoup
import base64
GOOGLE_CLIENT_ID = '****'
GOOGLE_CLIENT_SECRET = '***'
REDIRECT_URI = '/authorized' # one of the Redirect URIs from Google APIs console
SECRET_KEY = 'Uber'
DEBUG = True
app = Flask(__name__)
app.secret_key = 'Uber'
oauth = OAuth()
google = oauth.remote_app('google',
base_url='https://www.google.com/accounts/',
authorize_url='https://accounts.google.com/o/oauth2/auth',
request_token_url=None,
request_token_params={'scope': 'https://www.googleapis.com/auth/userinfo.email',
'response_type': 'code'},
access_token_url='https://accounts.google.com/o/oauth2/token',
access_token_method='POST',
access_token_params={'grant_type': 'authorization_code'},
consumer_key=GOOGLE_CLIENT_ID,
consumer_secret=GOOGLE_CLIENT_SECRET)
#app.route('/')
def index():
access_token = session.get('access_token')
if access_token is None:
return redirect(url_for('login'))
access_token = access_token[0]
from urllib2 import Request, urlopen, URLError
headers = {'Authorization': 'OAuth '+access_token}
req = Request('https://www.googleapis.com/oauth2/v1/userinfo',
None, headers)
try:
res = urlopen(req)
except URLError, e:
if e.code == 401:
# Unauthorized - bad token
session.pop('access_token', None)
return redirect(url_for('login'))
return res.read()
j = json.loads(res.read())
email_address = j['email']
print email_address, access_token
return Uber_Cost(email_address, access_token)
#app.route('/login')
def login():
callback=url_for('authorized', _external=True)
return google.authorize(callback=callback)
#app.route(REDIRECT_URI)
#google.authorized_handler
def authorized(resp):
access_token = resp['access_token']
session['access_token'] = access_token, ''
return redirect(url_for('index'))
#google.tokengetter
def get_access_token():
return session.get('access_token')
def GenerateOAuth2String(username, access_token, base64_encode=True):
auth_string = 'user=%s\1auth=Bearer %s\1\1' % (username, access_token)
if base64_encode:
auth_string = base64.b64encode(auth_string)
return auth_string
def Uber_Cost(email_address, access_token):
auth_string = GenerateOAuth2String(email_address, access_token, base64_encode=False)
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.debug = 4
mail.authenticate('XOAUTH2', lambda x: auth_string)
mail.select('INBOX')
It looks like you've got the authenticate() method down based on your latest code.
You also need the https://mail.google.com/ OAuth scope to authenticate with the IMAP and SMTP servers.
That is add it to your scope request, and make sure your app is configured for this scope on the Google App Console:
request_token_params={'scope': 'https://www.googleapis.com/auth/userinfo.email https://mail.google.com/',
'response_type': 'code'},
The Google OAUTH2 protocol and scopes are documented at their developer page.
Related
I am trying to make a chat app which is linked with a database which stores the users username and password but i would like to call the username in multiple functions. I currently have a global variable which is called NAME_KEY and i would like to replace it with the username of which ever user is logged in.
Here is my code as it currently stands!
from flask import render_template, redirect, session, request, url_for, jsonify, flash, Blueprint
from .database import DataBase
from flask_mysqldb import MySQL
from flask_socketio import SocketIO
import bcrypt
import MySQLdb
from config import *
view = Blueprint("views", __name__)
# GLOBAL CONSTANTS
NAME_KEY = 'username'
MSG_LIMIT = 20
socketio = SocketIO(app) # used for user communication
db = MySQL(app)
password = b"1234"
hashed = bcrypt.hashpw(password, bcrypt.gensalt())
# VIEWS
#view.route("/login", methods=["POST", "GET"])
def login():
"""
displays main login page and handles saving name in session
:exception POST
:return: None
"""
if request.method == 'POST':
if 'username' in request.form and 'password' in request.form:
username = request.form.get('username')
password = request.form.get('password').encode("utf-8")
cursor = db.connection.cursor(MySQLdb.cursors.DictCursor)
cursor.execute("SELECT * FROM logininfo WHERE email=%s AND password=%s", (username, password))
info = cursor.fetchone()
if info is not None:
if bcrypt.checkpw(password, hashed):
session['loginsuccess'] = True
flash(f'You were successfully logged in as {username}.')
return redirect(url_for('views.home'))
else:
flash("Profile doesn't exist please try again!")
return render_template("login.html", **{"session": session})
#view.route('/new/', methods=['GET', 'POST'])
def new_user():
if request.method == "POST":
if "one" in request.form and "two" in request.form and "three" in request.form:
username = request.form['one']
email = request.form['two']
password = request.form['three']
cur = db.connection.cursor(MySQLdb.cursors.DictCursor)
cur.execute("INSERT INTO login.logininfo(name, password, email)VALUES(%s, %s, %s)",
(username, password, email))
db.connection.commit()
return redirect(url_for('login'))
return render_template("register.html")
#view.route("/logout")
def logout():
"""
logs the user out by popping username from session
:return: None
"""
session.pop(NAME_KEY, None)
flash("0You were logged out.")
return redirect(url_for("views.login"))
#view.route("/")
#view.route("/home")
def home():
"""
displays home page if logged in
:return: None
"""
if NAME_KEY not in session:
return redirect(url_for("views.login"))
return render_template("index.html", **{"session": session})
#view.route("/history")
def history():
if NAME_KEY not in session:
flash("0Please login before viewing message history")
return redirect(url_for("views.login"))
json_messages = get_history(session[NAME_KEY])
print(json_messages)
return render_template("history.html", **{"history": json_messages})
#view.route("/get_name")
def get_name():
"""
:return: a json object storing name of logged in user
"""
data = {"name": ""}
if NAME_KEY in session:
data = {"name": session[NAME_KEY]}
return jsonify(data)
#view.route("/get_messages")
def get_messages():
"""
:return: all messages stored in database
"""
db = DataBase()
msgs = db.get_all_messages(MSG_LIMIT)
messages = remove_seconds_from_messages(msgs)
return jsonify(messages)
#view.route("/get_history")
def get_history(name):
"""
:param name: str
:return: all messages by name of user
"""
db = DataBase()
msgs = db.get_messages_by_name(name)
messages = remove_seconds_from_messages(msgs)
return messages
Currently this line of code is throwing an error
'cursor = db.connection.cursor(MySQLdb.cursors.DictCursor)'
The error is as follows
"KeyError: 'MYSQL_HOST'"
I also have no idea how to go about replacing the global variable NAME_KEY with one that takes which ever user is signed up and stored in my database.
I also have a config file which is as follows
from dotenv import load_dotenv
from pathlib import Path # python3 only
import os
from flask import Flask
# set path to env file
env_path = Path('.') / '.env'
load_dotenv(dotenv_path=env_path)
app = Flask(__name__)
class Config:
"""Set Flask configuration vars from .env file."""
# Load in environment variables
TESTING = os.getenv('TESTING')
FLASK_DEBUG = os.getenv('FLASK_DEBUG')
SECRET_KEY = os.getenv('SECRET_KEY')
SERVER = os.getenv('SERVER')
app.config["MYSQL_HOST"] = "login#localhost"
app.config["MYSQL_USER"] = "root"
app.config["MYSQL_PASSWORD"] = "pleasehelp"
app.config["MYSQL_DB"] = "login"
Any help or suggestions would be greatly appreciated.
Here are my errors
Traceback (most recent call last):
File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\flask\app.py", line 2464, in __call__
return self.wsgi_app(environ, start_response)
File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\flask_socketio\__init__.py", line 46, in __call__
start_response)
File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\engineio\middleware.py", line 71, in __call__
return self.wsgi_app(environ, start_response)
File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\flask\app.py", line 2450, in wsgi_app
response = self.handle_exception(e)
File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\flask\app.py", line 1867, in handle_exception
reraise(exc_type, exc_value, tb)
File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\flask\_compat.py", line 39, in reraise
raise value
File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\flask\app.py", line 2447, in wsgi_app
response = self.full_dispatch_request()
File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\flask\app.py", line 1952, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\flask\app.py", line 1821, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\flask\_compat.py", line 39, in reraise
raise value
File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\flask\app.py", line 1950, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\flask\app.py", line 1936, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "C:\Users\User\PycharmProjects\testApp\testApp\application\views.py", line 40, in login
cursor = db.connection.cursor(MySQLdb.cursors.DictCursor)
File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\flask_mysqldb\__init__.py", line 94, in connection
ctx.mysql_db = self.connect
File "C:\Users\User\AppData\Local\Programs\Python\Python37\lib\site-packages\flask_mysqldb\__init__.py", line 43, in connect
if current_app.config['MYSQL_HOST']:
KeyError: 'MYSQL_HOST'
I'm building an SMS based chatbot using Python, Twilio, Flask, IBM Watson and Google App Engine. It works fine for a few minutes but then inevitably sends up an Internal Response Error.
I've tried editing the yaml file so it has a default expiration of 1 day. It doesn't do anything.
Edit: Currently on automatic scaling, but I've tried changing from autmatic to basic scaling, and changing min_instances to 1.
Here's the log:
2019-07-06 08:57:09 default[20190706t180659]
Traceback (most recent call last):
File "/env/lib/python3.7/site-packages/flask/app.py", line 2292, in wsgi_app
response = self.full_dispatch_request()
File "/env/lib/python3.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/env/lib/python3.7/site-packages/flask/app.py", line 1718, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/env/lib/python3.7/site-packages/flask/_compat.py", line 35, in reraise
raise value
File "/env/lib/python3.7/site-packages/flask/app.py", line 1813, in full_dispatch_request
rv = self.dispatch_request()
File "/env/lib/python3.7/site-packages/flask/app.py", line 1799, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/srv/main.py", line 81, in incoming_sms
r = Watson(b)
File "/srv/main.py", line 42, in Watson
input = message_input
File "/srv/ibm_watson/assistant_v2.py", line 244, in message
accept_json=True)
File "/srv/ibm_cloud_sdk_core/base_service.py", line 358, in request
raise ApiException(response.status_code, error_message, http_response=response)
ibm_cloud_sdk_core.api_exception.ApiException: Error: NotFound: session id 2900da9f-dd77-480a-a939-1a5b060b3f82 for agent instance 6656a86c-ad0e-4463-8344-5f7fdcb4a6fe, Code: 404 , X-global-transaction-id: 5a3699f1cd4e3409e9f89f4fcd87735f
Code to take inputs on Twilio, feed them through Watson and respond.
from flask import Flask, request, make_response
from twilio.twiml.messaging_response import MessagingResponse
import os
from twilio import twiml
import ibm_watson
# Set up Assistant service.
service = ibm_watson.AssistantV2(
iam_apikey = 'xxx', # replace with API key
version = '2019-02-28',
url = 'https://gateway-syd.watsonplatform.net/assistant/api')
assistant_id = 'xxx' #Under Assistant Settings
# Create session.
session_id = service.create_session(
assistant_id = assistant_id
).get_result()['session_id']
def Watson(b):
# Initialize with empty value to start the conversation.
message_input = {
'message_type:': 'text',
'text': str(b)
}
# Main input/output loop
while message_input['text'] != 'quitt':
# Send message to assistant.
response = service.message(
assistant_id,
session_id,
input = message_input
).get_result()
# If an intent was detected, print it to the console.
if response['output']['intents']:
print('Detected intent: #' + response['output']['intents'][0]['intent'])
# Print the output from dialog, if any. Supports only a single
# text response.
if response['output']['generic']:
if response['output']['generic'][0]['response_type'] == 'text':
return(response['output']['generic'][0]['text'])
# Prompt for next round of input.
message_input = {
'text': str(b)
}
# We're done, so we delete the session.
service.delete_session(
assistant_id = assistant_id,
session_id = session_id
)
app = Flask(__name__)
#app.route("/sms", methods=['GET', 'POST'])
def incoming_sms():
"""Send a dynamic reply to an incoming text message"""
#Get the message the user sent our Twilio number
body = request.values.get('Body', None)
b = str(body)
b = b.lower()
resp = MessagingResponse()
r = Watson(b)
resp.message(r)
# response.append(r)
return str(resp)
if __name__ == "__main__":
app.run(debug=True)
You're getting an exception from the ibm_cloud_sdk_core package. This could possibly be due to App Engine scaling up to multiple instances, but session IDs not being shared across instances. So instance A might have a session ID, but a subsequent request might go to instance B which does not have this session ID.
You should reach out to the library maintainers to understand how to either pool session IDs, or determine whether you have a valid session ID for a request before making the request to their API.
I have the below code for app.py:
from flask import Flask
from flask_jwt import JWT, jwt_required, current_identity
from werkzeug.security import safe_str_cmp
class User(object):
def __init__(self, id, username, password):
self.id = id
self.username = username
self.password = password
def __str__(self):
return "User(id='%s')" % self.id
users = [
User(1, 'user1', 'abcxyz'),
User(2, 'user2', 'abcxyz'),
]
username_table = {u.username: u for u in users}
userid_table = {u.id: u for u in users}
def authenticate(username, password):
user = username_table.get(username, None)
if user and safe_str_cmp(user.password.encode('utf-8'), password.encode('utf-8')):
return user
def identity(payload):
user_id = payload['identity']
return userid_table.get(user_id, None)
app = Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = 'super-secret'
jwt = JWT(app, authenticate, identity)
#app.route('/protected')
#jwt_required()
def protected():
return '%s' % current_identity
if __name__ == '__main__':
app.run()
When i try to make a post request to get the authentication token from Postman :
http://127.0.0.1:5000/auth
{
"username": "joe",
"password": "pass"
}
I get a peculiar error which i am not able to solve :
(flask-restful) C:\Users\bhatsubh\Projects\flask-restful>python Test.py
* Restarting with stat
* Debugger is active!
* Debugger PIN: 973-755-004
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [06/Sep/2017 13:38:08] "POST /auth HTTP/1.1" 500 -
Traceback (most recent call last):
File "C:\Users\bhatsubh\Envs\flask-restful\lib\site-packages\flask\app.py", li
ne 1997, in __call__
return self.wsgi_app(environ, start_response)
File "C:\Users\bhatsubh\Envs\flask-restful\lib\site-packages\flask\app.py", li
ne 1985, in wsgi_app
response = self.handle_exception(e)
File "C:\Users\bhatsubh\Envs\flask-restful\lib\site-packages\flask\app.py", li
ne 1540, in handle_exception
reraise(exc_type, exc_value, tb)
File "C:\Users\bhatsubh\Envs\flask-restful\lib\site-packages\flask\_compat.py"
, line 33, in reraise
raise value
File "C:\Users\bhatsubh\Envs\flask-restful\lib\site-packages\flask\app.py", li
ne 1982, in wsgi_app
response = self.full_dispatch_request()
File "C:\Users\bhatsubh\Envs\flask-restful\lib\site-packages\flask\app.py", li
ne 1614, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\Users\bhatsubh\Envs\flask-restful\lib\site-packages\flask\app.py", li
ne 1517, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "C:\Users\bhatsubh\Envs\flask-restful\lib\site-packages\flask\_compat.py"
, line 33, in reraise
raise value
File "C:\Users\bhatsubh\Envs\flask-restful\lib\site-packages\flask\app.py", li
ne 1612, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Users\bhatsubh\Envs\flask-restful\lib\site-packages\flask\app.py", li
ne 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "C:\Users\bhatsubh\Envs\flask-restful\lib\site-packages\flask_jwt\__init_
_.py", line 115, in _default_auth_request_handler
username = data.get(current_app.config.get('JWT_AUTH_USERNAME_KEY'), None)
AttributeError: 'NoneType' object has no attribute 'get'
I have absolutely no idea what is going wrong. Can someone please help me with this?
make sure have set correct header
Content-Type:application/json
try curl for cross check
$ curl -H "Content-Type: application/json" -X POST -d '{ "username": "joe", "password": "pass" }' http://localhost:5000/auth
I suspect you’re being thrown off by this because there’s a bug in flask-jwt which is throwing up errors rather than providing proper feedback. So it’s hard to know what’s up.
If there are any header errors, it triggers errors rather than a proper response.
Dave B’s suggestion worked for me, setting the header with: Content-Type:application/json
I raised this with the developers, so hopefully, they can patch it up.
Look at https://github.com/vimalloc/flask-jwt-extended or https://github.com/vimalloc/flask-jwt-simple as alternatives to Flask-JWT. They are better designed, up to date, and still maintained (I am the author, so I am of course biased).
try this:
localhost:5000/auth
{ "username": "user1", "password": "abcxyz" }
I'm testing various oath2 workflows and the one I'm strugglink to, is Authorization Code Grant.
I can successfully get a token If I copy/paste urls in a straight forward way.
I mean request access, copy paste given URl, enter it in a browser, accept authorization, copy paste back callback url -> access resources. Like in this example:
from requests_oauthlib import OAuth2Session
class ClientSecrets:
"""
The structure of this class follows Google convention for `client_secrets.json`:
https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
Bitbucket does not emit this structure so it must be manually constructed.
"""
client_id = "myid"
client_secret = "mysecret"
auth_uri = "https://bitbucket.org/site/oauth2/authorize"
token_uri = "https://bitbucket.org/site/oauth2/access_token"
server_base_uri = "https://api.bitbucket.org/"
def main():
c = ClientSecrets()
# Fetch a request token
bitbucket = OAuth2Session(c.client_id)
# Redirect user to Bitbucket for authorization
authorization_url = bitbucket.authorization_url(c.auth_uri)
print('Please go here and authorize: {}'.format(authorization_url[0]))
# Get the authorization verifier code from the callback url
redirect_response = raw_input('Paste the full redirect URL here:')
# Fetch the access token
bitbucket.fetch_token(
c.token_uri,
authorization_response=redirect_response,
username=c.client_id,
password=c.client_secret)
# Fetch a protected resource, i.e. user profile
r = bitbucket.get(c.server_base_uri + '1.0/user')
print(r.content)
Though if I try to do that using Flask imitating actual web app that tries to get access to bitbucket, it fails to get access.
My Flask app implementation sample looks like this:
from flask import Flask, redirect, request, session
from requests_oauthlib import OAuth2Session
app = Flask(__name__)
client_id = 'myid'
client_secret = 'mysecret'
authorization_base_url = 'https://bitbucket.org/site/oauth2/authorize'
token_url = 'https://bitbucket.org/site/oauth2/access_token'
redirect_uri = 'https://127.0.0.1:5000/callback'
#app.route('/login')
def login():
oauth2 = OAuth2Session(client_id, redirect_uri=redirect_uri)
authorization_url, state = oauth2.authorization_url(
authorization_base_url,
)
# State is used to prevent CSRF, keep this for later.
session['oauth_state'] = state
return redirect(authorization_url)
#app.route("/callback")
def callback():
bitbucket = OAuth2Session(client_id, state=session['oauth_state'])
bitbucket.fetch_token(
token_url,
client_secret=client_secret,
authorization_response=request.url)
return bitbucket.get('some_resource_url').content
if __name__ == '__main__':
# Certificate and key files.
context = ('cert/server.crt', 'cert/server.key')
app.run(debug=True, ssl_context=context)
If I run app like in example, then I get this error when trying to access URL: https://127.0.0.1:5000/login
File "/home/oerp/python-programs/flask-app/bitiface/test.py", line 23, in login
session['oauth_state'] = state
File "/home/oerp/python-programs/flask-app/bitiface/venv/lib/python2.7/site-packages/werkzeug/local.py", line 350, in __setitem__
self._get_current_object()[key] = value
File "/home/oerp/python-programs/flask-app/bitiface/venv/lib/python2.7/site-packages/flask/sessions.py", line 130, in _fail
raise RuntimeError('The session is unavailable because no secret '
RuntimeError: The session is unavailable because no secret key was set. Set the secret_key on the application to something unique and secret.
It looks like that something todo with oath_state. If I comment oath_state assigning and try to run app like before. Then I get this error:
Traceback (most recent call last):
File "/home/oerp/python-programs/flask-app/bitiface/venv/lib/python2.7/site-packages/flask/app.py", line 1994, in __call__
return self.wsgi_app(environ, start_response)
File "/home/oerp/python-programs/flask-app/bitiface/venv/lib/python2.7/site-packages/flask/app.py", line 1985, in wsgi_app
response = self.handle_exception(e)
File "/home/oerp/python-programs/flask-app/bitiface/venv/lib/python2.7/site-packages/flask/app.py", line 1540, in handle_exception
reraise(exc_type, exc_value, tb)
File "/home/oerp/python-programs/flask-app/bitiface/venv/lib/python2.7/site-packages/flask/app.py", line 1982, in wsgi_app
response = self.full_dispatch_request()
File "/home/oerp/python-programs/flask-app/bitiface/venv/lib/python2.7/site-packages/flask/app.py", line 1614, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/home/oerp/python-programs/flask-app/bitiface/venv/lib/python2.7/site-packages/flask/app.py", line 1517, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/home/oerp/python-programs/flask-app/bitiface/venv/lib/python2.7/site-packages/flask/app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "/home/oerp/python-programs/flask-app/bitiface/venv/lib/python2.7/site-packages/flask/app.py", line 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/home/oerp/python-programs/flask-app/bitiface/test.py", line 33, in callback
authorization_response=request.url)
File "/home/oerp/python-programs/flask-app/bitiface/venv/lib/python2.7/site-packages/requests_oauthlib/oauth2_session.py", line 244, in fetch_token
self._client.parse_request_body_response(r.text, scope=self.scope)
File "/home/oerp/python-programs/flask-app/bitiface/venv/lib/python2.7/site-packages/oauthlib/oauth2/rfc6749/clients/base.py", line 409, in parse_request_body_response
self.token = parse_token_response(body, scope=scope)
File "/home/oerp/python-programs/flask-app/bitiface/venv/lib/python2.7/site-packages/oauthlib/oauth2/rfc6749/parameters.py", line 376, in parse_token_response
validate_token_parameters(params)
File "/home/oerp/python-programs/flask-app/bitiface/venv/lib/python2.7/site-packages/oauthlib/oauth2/rfc6749/parameters.py", line 383, in validate_token_parameters
raise_from_error(params.get('error'), params)
File "/home/oerp/python-programs/flask-app/bitiface/venv/lib/python2.7/site-packages/oauthlib/oauth2/rfc6749/errors.py", line 325, in raise_from_error
raise cls(**kwargs)
InvalidClientIdError: (invalid_request) redirect_uri does not match
It looks like if I skip oath_session, then it actually starts new session on callback and redirect_uri will not match or something like that.
Does anyone know what could be the problem?
Update
I updated Flask with secret_key, so error regarding no secret_key is gone, but the second error is still there. This error: InvalidClientIdError: (invalid_request) redirect_uri does not match is raised at this part of code:
...
...
bitbucket.fetch_token(
token_url,
client_secret=client_secret,
authorization_response=request.url)
...
...
I do not know if this is related, but printing request.url, it gives me this: https://127.0.0.1:5000/callback?state=[bunch_of_random_symbols]. So the first part https://127.0.0.1:5000/callback is exactly the same as I I set on consumer to be callback URL. To me it looks like it is actually matching.
`
P.S. Full traceback is provided.
The reason of the main error is redirect_uri argument which is present in authorization URL but not passed in access token request. As RFC 6749 describes:
redirect_uri
REQUIRED, if the "redirect_uri" parameter was included in the authorization request as described in Section 4.1.1, and their values MUST be identical.
So you need to initialize your callback OAuth session with redirect_uri:
#app.route("/callback")
def callback():
bitbucket = OAuth2Session(
client_id, state=session['oauth_state'], redirect_uri=redirect_uri
)
It is rather common question among developers why we need to send redirect_uri for getting access token. This discussion on Security.Stackexchange could be helpful.
As for the first error:
RuntimeError: The session is unavailable because no secret key was set. Set the secret_key on the application to something unique and secret.
It is raised when you try to save some information in Flask session but secret key for Flask application was not set.
This is my code for my main.py file which is designed to be a simple contact form built in flask.
from flask import Flask, render_template, request
from flask_mail import Mail, Message
from forms import ContactForm
app = Flask(__name__)
app.secret_key = 'YourSuperSecreteKey'
# add mail server config
app.config['MAIL_SERVER'] = 'smtp.gmail.com'
app.config['MAIL_PORT'] = 465
app.config['MAIL_USE_SSL'] = True
app.config['MAIL_USERNAME'] = 'YourUser#NameHere'
app.config['MAIL_PASSWORD'] = 'yourMailPassword'
mail = Mail(app)
#app.route('/')
def hello():
"""Return a friendly HTTP greeting."""
return 'Hello World!'
#app.errorhandler(404)
def page_not_found(e):
"""Return a custom 404 error."""
return 'Sorry, Nothing at this URL.', 404
#app.errorhandler(500)
def application_error(e):
"""Return a custom 500 error."""
return 'Sorry, unexpected error: {}'.format(e), 500
#app.route('/contact', methods=('GET', 'POST'))
def contact():
form = ContactForm()
if request.method == 'POST':
if form.validate() == False:
return 'Please fill in all fields <p>Try Again!!!</p>'
else:
msg = Message("Message from your visitor" + form.name.data,
sender='YourUser#NameHere',
recipients=['yourRecieve#mail.com', 'someOther#mail.com'])
msg.body = """
From: %s <%s>,
%s
""" % (form.name.data, form.email.data, form.message.data)
mail.send(msg)
return "Successfully sent message!"
elif request.method == 'GET':
return render_template('contact.html', form=form)
if __name__ == '__main__':
app.run()
I get the error: Sorry, unexpected error: 'module' object has no attribute 'SMTP_SSL'
I've called my file "main.py". Everything works fine until I try and send the actual email. Is this just because I haven't populated the settings or is something else a miss?
Sorry just figured out how to see traceback on GAE:
Exception on /contact [POST]
Traceback (most recent call last):
File "/base/data/home/apps/s~smart-cove-95709/1.384663697853252774/lib/flask/app.py", line 1817, in wsgi_app
response = self.full_dispatch_request()
File "/base/data/home/apps/s~smart-cove-95709/1.384663697853252774/lib/flask/app.py", line 1477, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/base/data/home/apps/s~smart-cove-95709/1.384663697853252774/lib/flask/app.py", line 1381, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/base/data/home/apps/s~smart-cove-95709/1.384663697853252774/lib/flask/app.py", line 1475, in full_dispatch_request
rv = self.dispatch_request()
File "/base/data/home/apps/s~smart-cove-95709/1.384663697853252774/lib/flask/app.py", line 1461, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/base/data/home/apps/s~smart-cove-95709/1.384663697853252774/main.py", line 50, in contact
mail.send(msg)
File "/base/data/home/apps/s~smart-cove-95709/1.384663697853252774/lib/flask_mail.py", line 491, in send
with self.connect() as connection:
File "/base/data/home/apps/s~smart-cove-95709/1.384663697853252774/lib/flask_mail.py", line 144, in __enter__
self.host = self.configure_host()
File "/base/data/home/apps/s~smart-cove-95709/1.384663697853252774/lib/flask_mail.py", line 156, in configure_host
host = smtplib.SMTP_SSL(self.mail.server, self.mail.port)
AttributeError: 'module' object has no attribute 'SMTP_SSL'
You have set the MAIL_USE_SSL option to True:
app.config['MAIL_USE_SSL'] = True
which means that the Flask-Mail extension will want to use the smtplib.SMTP_SSL class, but that class isn't usually available on Google App Engine.
That class is only available if the Python ssl module is available, which is only the case if your Python was built with SSL support available. This isn't normally the case in the GAE environment.
The module is not available on Google App Engine unless you specifically enable it. Enable it in your app.yaml:
libraries:
- name: ssl
version: latest
Do note that the socket support on GAE is experimental.
For sending email on GAE you are better off using the mail.send_mail() function however, so you can make use of the GAE infrastructure instead.