JWT token generator with Authlib 0.11 - python

First: A big thanks to the Authlib creator / other open source creators and supporters.
I would like to have Authlib 0.11 to return oauth tokens as JWT.
I tried to follow the documentation provided in Authlib website to create a JWT token generator with Authlib 0.11 https://docs.authlib.org/en/latest/flask/2/authorization-server.html#token.
Since, I am a novice user in this topic I still couldn't figure out the right way to pass my JWT token generator method to the config:OAUTH2_ACCESS_TOKEN_GENERATOR
Any help is appreciated.
Here is my dummy jwt token generator:
from authlib.jose import jwt
def gen_access_token(client, grant_type, user, scope):
log.debug('Not used yet in the JWT:: {} \n{} \n{} \n{}'.format( client, grant_type, user, scope))
header = {'alg': 'RS256'}
payload = {
'iss': 'http://127.0.0.1:5000/oauth/token',
'sub': 'test client',
'aud': 'profile'
}
try:
key = open('wf-app-server.key', 'r').read()
s = jwt.encode(header, payload, key)
claims = jwt.decode(s, open('wf-app-pub.pem', 'r').read())
except Exception as e:
log.debug('JWT exception', e)
log.debug("jwt encoded:{}\n decoded :{} \n header:{}".format(
s, claims, claims.header))
return s
OAUTH2_REFRESH_TOKEN_GENERATOR = True
OAUTH2_TOKEN_EXPIRES_IN = {
'authorization_code': 874000,
'implicit': 3600,
'password': 600000,
'client_credentials': 600000
}
OAUTH2_ACCESS_TOKEN_GENERATOR = gen_access_token('bCsNV2Lo8hxD593Km84lWM5d', 'client_credentials', 'admin', 'profile')
-- output showing my JWT token generator works and the returned value can be decoded correctly --
2019-06-22 13:37:38,024 DEBUG gen_access_token (7) Not used yet in the JWT:: bCsNV2Lo8hxD593Km84lWM5d client_credentials admin profile
2019-06-22 13:37:38,052 DEBUG gen_access_token (21) jwt encoded:b'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjUwMDAvb2F1dGgvdG9rZW4iLCJzdWIiOiJ0ZXN0IGNsaWVudCIsImF1ZCI6InByb2ZpbGUifQ.BU5dSbPAFzoDDo4vathd6jlQVmDHaygEUh4GCwknCdbf4AVig3SgOW8JbITuPCKTf7qnxE8iJCWUOAd_wDCZwWKXdpisG6EGGmNpwZLAsDqL1CLgqTsRuGrc2kUfyMOHXfGXGkqsNROuPFV0-XYgxCQOz4LolNcB3Knvu1ApRcZyej8nAFXKxccDkLYyhldjRJwRehRZ4tMjDlbP4ghmEUFBF1Msx5Yzot26IK3ps4dfLnYVJr2dKUIPK75BzYR5kgUm3nkJRe4F0898j8tIMZwvKa2lKSypORDQXUxC3i8-x7A2vsVk7Jw3qcbZBarqstUEWITCZSVPYoHoF5l8iw'
decoded :{'iss': 'http://127.0.0.1:5000/oauth/token', 'sub': 'test client', 'aud': 'profile'} header:{'alg': 'RS256', 'typ': 'JWT'}
First, to test whether my oauth token request credentials are correct, I tried to request a oauth token with right client_credentials and the default token_generator from Authlib. With this I got the default oauth token.
Second, I updated the config with my token generator, then when I request an oauth token with the same client credentials, then I get the following error:
2019-06-22 13:40:56,700 DEBUG authenticate_client_secret_basic (65)
Authenticate bCsNV2Lo8hxD593Km84lWM5d via "client_secret_basic"
success
I created this custom debug line below to understand what the default access_token_generator() takes as input parameters. It is exactly take the same types - my input parameter types also match!
2019-06-22 13:40:56,701 DEBUG validate_token_request (67)
Validate token request of <OAuth2Client 2> client: <OAuth2Client 2>
type:<class 'website.models.OAuth2Client'> grant_type:
client_credentials type:<class 'str'> user: None type:<class
'NoneType'> scope: rs1secret type:<class 'str'>
2019-06-22 13:40:56,708 INFO _log (122) 127.0.0.1 - - [22/Jun/2019 13:40:56] "POST /oauth/token HTTP/1.1" 500 - Traceback (most recent call last): File "/home/pksec/.local/share/virtualenvs/oAuthProvider-n_KOMqPA/lib/python3.7/site-packages/flask/app.py",
line 2328, in __call__
return self.wsgi_app(environ, start_response) File "/home/pksec/.local/share/virtualenvs/oAuthProvider-n_KOMqPA/lib/python3.7/site-packages/flask/app.py",
line 2314, in wsgi_app
response = self.handle_exception(e) File "/home/pksec/.local/share/virtualenvs/oAuthProvider-n_KOMqPA/lib/python3.7/site-packages/flask/app.py",
line 1760, in handle_exception
reraise(exc_type, exc_value, tb) File "/home/pksec/.local/share/virtualenvs/oAuthProvider-n_KOMqPA/lib/python3.7/site-packages/flask/_compat.py",
line 36, in reraise
raise value File "/home/pksec/.local/share/virtualenvs/oAuthProvider-n_KOMqPA/lib/python3.7/site-packages/flask/app.py",
line 2311, in wsgi_app
response = self.full_dispatch_request() File "/home/pksec/.local/share/virtualenvs/oAuthProvider-n_KOMqPA/lib/python3.7/site-packages/flask/app.py",
line 1834, in full_dispatch_request
rv = self.handle_user_exception(e) File "/home/pksec/.local/share/virtualenvs/oAuthProvider-n_KOMqPA/lib/python3.7/site-packages/flask/app.py",
line 1737, in handle_user_exception
reraise(exc_type, exc_value, tb) File "/home/pksec/.local/share/virtualenvs/oAuthProvider-n_KOMqPA/lib/python3.7/site-packages/flask/_compat.py",
line 36, in reraise
raise value File "/home/pksec/.local/share/virtualenvs/oAuthProvider-n_KOMqPA/lib/python3.7/site-packages/flask/app.py",
line 1832, in full_dispatch_request
rv = self.dispatch_request() File "/home/pksec/.local/share/virtualenvs/oAuthProvider-n_KOMqPA/lib/python3.7/site-packages/flask/app.py",
line 1818, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args) File "/home/pksec/xx/oAuthProvider/website/routes.py",
line 193, in issue_token
return authorization.create_token_response() File "/virtualenvs/oAuthProvider-n_KOMqPA/lib/python3.7/site-packages/authlib/oauth2/rfc6749/authorization_server.py",
line 186, in create_token_response
args = grant.create_token_response() File "/home/pksec/.local/share/virtualenvs/oAuthProvider-n_KOMqPA/lib/python3.7/site-packages/authlib/oauth2/rfc6749/grants/client_credentials.py",
line 104, in create_token_response
include_refresh_token=False, File "/home/pksec/.local/share/virtualenvs/oAuthProvider-n_KOMqPA/lib/python3.7/site-packages/authlib/oauth2/rfc6749/grants/base.py",
line 58, in generate_token
include_refresh_token=include_refresh_token, File "/home/pksec/.local/share/virtualenvs/oAuthProvider-n_KOMqPA/lib/python3.7/site-packages/authlib/oauth2/rfc6750/wrappers.py",
line 91, in __call__
access_token = self.access_token_generator(client, grant_type, user, scope) TypeError: 'NoneType' object is not callable
I know that I am doing something wrong when I pass the my gen_acc_token() method to the config - but couldn't exactly figure out what is wrong.
A small code snippet with that passes a sample gen_JWT_access_token() would be great.

I figure out finally the right way to pass my JWT token generator method to the config:OAUTH2_ACCESS_TOKEN_GENERATOR
Here is my dummy jwt token generator:
from authlib.jose import jwt
def gen_access_token(client, grant_type, user, scope):
log.debug('Not used yet in the JWT:: {} \n{} \n{} \n{}'.format( client, grant_type, user, scope))
header = {'alg': 'RS256'}
payload = {
'iss': 'http://127.0.0.1:5000/oauth/token',
'sub': 'test client',
'aud': 'profile'
}
try:
key = open('wf-app-server.key', 'r').read()
s = jwt.encode(header, payload, key)
claims = jwt.decode(s, open('wf-app-pub.pem', 'r').read())
except Exception as e:
log.debug('JWT exception', e)
log.debug("jwt encoded:{}\n decoded :{} \n header:{}".format(
s, claims, claims.header))
return s
OAUTH2_REFRESH_TOKEN_GENERATOR = True
OAUTH2_TOKEN_EXPIRES_IN = {
'authorization_code': 874000,
'implicit': 3600,
'password': 600000,
'client_credentials': 600000
}
OAUTH2_ACCESS_TOKEN_GENERATOR = gen_access_token
Do not Pass the function parameters: Python NoneType object is not callable (beginner)
This is a beginner mistake! Follow your error output, you will find the solution!
This is how you should not pass your generator function:
OAUTH2_ACCESS_TOKEN_GENERATOR = gen_access_token('bCsNV2Lo8hxD593Km84lWM5d', 'client_credentials', 'admin', 'profile')

Related

How do I specify x-apikeyInfoFunc in swager securityDefinitions?

I have openapi that defines API with this securityDefinitions:
securityDefinitions:
APIKeyHeader:
type: apiKey
in: header
name: Authorization
security:
- APIKeyHeader: []
When I start the project I get this warning:
WARNING 2022-01-27 13:24:41,001 connexion.operations.secure security_decorator 142 : ... x-apikeyInfoFunc missing
And such error when I try to use API methods:
INFO 2022-01-27 13:56:15,256 connexion.api.security get_authorization_info 131 : ... No auth provided. Aborting with 401.
I found that I need to specify x-apikeyInfoFunc in securityDefinitions. I specified a function that I believe does authentication:
securityDefinitions:
APIKeyHeader:
type: apiKey
in: header
name: Authorization
x-apikeyInfoFunc: util.authentication_decorator.authenticate
security:
- APIKeyHeader: []
The function itself:
def authenticate(arg: Optional[Sequence[str]] = DEFAULT_SCOPE):
""" decorator to handle api key authentication """
def decorator(fun):
""" decorator that gets applied to the function """
def wrapper(*a, **kw):
""" function wrapper """
# pylint: disable=unused-argument
api_key = request.headers.get('Authorization')
if validate_scope(api_key, scopes):
# return fun(*a, **kw)
return fun()
LOGGER.debug('Invalid or missing API key in request')
return {'msg': 'Make sure you supply your API key with sufficient scope in the Authorization header'}, 403
return wrapper
if callable(arg):
scopes = DEFAULT_SCOPE
return decorator(arg)
scopes = arg
return decorator
The function is used as a decorator to authenticate every API method. When I start the project I don't get warning. But I get another error when I actually trying to use one of API method:
ERROR 2022-01-28 13:50:03,330 openapi_helper.app_helper log_exception 1891: Exception on /v1/jira/search_issues_by_tags [GET]
Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/flask/app.py", line 2446, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python3.6/site-packages/flask/app.py", line 1951, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python3.6/site-packages/flask/app.py", line 1820, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python3.6/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/usr/local/lib/python3.6/site-packages/flask/app.py", line 1949, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python3.6/site-packages/flask/app.py", line 1935, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/usr/local/lib/python3.6/site-packages/connexion/decorators/decorator.py", line 48, in wrapper
response = function(request)
File "/usr/local/lib/python3.6/site-packages/connexion/decorators/security.py", line 322, in wrapper
token_info = get_authorization_info(auth_funcs, request, required_scopes)
File "/usr/local/lib/python3.6/site-packages/connexion/decorators/security.py", line 127, in get_authorization_info
token_info = func(request, required_scopes)
File "/usr/local/lib/python3.6/site-packages/connexion/decorators/security.py", line 284, in wrapper
token_info = apikey_info_func(apikey, required_scopes=required_scopes)
TypeError: authenticate() got an unexpected keyword argument 'required_scopes'
I'm stuck on this point, don't have idea how to proceed. connexion 2.6.0 is used in this case.
According to Connexion docs, the x-apikeyInfoFunc function must have two parameters: apikey and required_scopes.
Example 1
Example 2

Oauth2 in Python with Flask gets 302 from API

I am trying to access MeisterTask's API with Python and Flask, and no matter what I do, I seem to always get a 302 code in return from the API, although I can get an access token (or so I think). Here is the code I have so far (I tried reducing it, this is the smallest snippet I could get that replicates the error):
from flask import Flask, redirect, url_for, session, request, jsonify
from flask_oauthlib.client import OAuth
app = Flask(__name__)
app.debug = True
app.secret_key = "development"
oauth = OAuth(app)
meistertask = oauth.remote_app(
'meistertask',
consumer_key= "XXXXXX",
consumer_secret= "XXXXXX",
request_token_params={"scope" : "meistertask"},
base_url='https://www.meistertask.com/api',
request_token_url=None,
access_token_method='GET',
access_token_url='https://www.mindmeister.com/login/oauth2/token',
authorize_url='https://www.mindmeister.com/oauth2/authorize'
)
#app.route('/')
def index():
if 'meistertask_token' in session:
me = meistertask.get('user')
return jsonify(me.data)
return redirect(url_for('login'))
#app.route('/login')
def login():
return meistertask.authorize(callback=url_for('authorized', _external=True))
#app.route('/logout')
def logout():
session.pop('meistertask_token', None)
return redirect(url_for('index'))
#app.route('/login/authorized')
def authorized():
resp = meistertask.authorized_response()
print(resp.get('code'))
if resp is None or resp.get('code') is None:
return 'Access denied: reason=%s error=%s resp=%s' % (
request.args['error'],
request.args['error_description'],
resp
)
session['meistertask_token'] = (resp['code'], '')
return "Hello"
#meistertask.tokengetter
def get_meistertask_oauth_token():
return session.get('meistertask_token')
if __name__ == "__main__":
app.run()
And here is the traceback:
flask_oauthlib.client.OAuthException: Invalid response from meistertask
Traceback (most recent call last):
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 2309, in __call__ return self.wsgi_app(environ, start_response)
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 2295, in wsgi_app response = self.handle_exception(e)
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 1741, in handle_exception reraise(exc_type, exc_value, tb)
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\_compat.py", line 35, in reraise raise value
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 2292, in wsgi_app response = self.full_dispatch_request()
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 1815, in full_dispatch_request rv = self.handle_user_exception(e)
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 1718, in handle_user_exception reraise(exc_type, exc_value, tb)
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\_compat.py", line 35, in reraise raise value
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 1813, in full_dispatch_request rv = self.dispatch_request()
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask\app.py", line 1799, in dispatch_request return self.view_functions[rule.endpoint](**req.view_args)
File "~\Documents\MeisterTaskServer\hello.py", line 49, in authorized resp = meistertask.authorized_response()
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask_oauthlib\client.py", line 707, in authorized_response data = self.handle_oauth2_response(args)
File "~\AppData\Local\Programs\Python\Python37-32\lib\site-packages\flask_oauthlib\client.py", line 692, in handle_oauth2_response
Things I have tried
Tried to modify the method to get the access token from GET to POST. The API clearly states that I should use GET, yet every other example I have seen on flask_oauthlib's GitHub uses POST (the examples are 3 years old, but some still work, namely the GitHub one). Either give the same result.
Tried doing it barebones, without any library. The resulting code was thrice as long and also had more problems.
Used Django instead of Flask. Never even managed to get the hello world example going, it was too much work, and also I have discovered the library flask_oauthlib.
Things worth mentioning
I derived this code from this here GitHub example
There is also code there I omitted in order to keep the snippet short, that establishes that the server should use SSL (as per the request from the API that the redirect_uri should use HTTPS)
The app manages to redirect me over at MeisterTask and asks for my permission. Once I grant it, it redirects to "https://127.0.0.1:5000/login/authorized?code=some_token" where I get the traceback. If I look with Chrome's debugging tools to the requests made and what I receive, I see that I get an 302 from the API, but I also get an access token.
I run Windows 10 with Python 3.7.0
So what's the deal? What's the next step here? I've run out of things to try. Thank you for taking the time to solve this!

'NoneType' object is not subscriptable [duplicate]

This question already has answers here:
How to get POSTed JSON in Flask?
(13 answers)
Closed 6 years ago.
I am creating an ios app that uses a server written in flask + python, and when I make a connection to the server to register a user I keep getting a 'NoneType' object is not subscriptable error in my server.py file. Basically my question is what is causing this error and how am I able to fix this. Also if anyone can point me in the right direction of different or easier ways to do this I would appreciate it thanks!
Here is the server.py file:
import bcrypt
from flask import Flask, request, make_response,jsonify
from flask_restful import Resource, Api
from pymongo import MongoClient
from json import JSONEncoder
from bson.objectid import ObjectId
from functools import wraps
app = Flask(__name__)
mongo = MongoClient('localhost', 27017)
app.db = mongo.eventure_db
app.bcrypt_rounds = 12
api = Api(app)
# Authentication code.
def check_auth(username, password):
# check_auth should access the database and check if the username + password are correct.
# create a collection to hold the users.
user_collection = app.db.users
user = user_collection.find_one({'username': username})
if user is None:
return False
else:
# check if hash generated matches stored hash
encodedPassword = password.encode('utf-8')
if bcrypt.hashpw(encodedPassword, user['password']) == user['password']:
return True
else:
return False
# User resource
class User(Resource):
def post(self):
if (request.json['username'] == None
or request.json['password'] == None):
return ({'error': 'Request requires username and password'},
400,
None)
user_collection = app.db.users
user = user_collection.find_one({'username': request.json['username']})
if user is not None:
return ({'error': 'Username already in use'}, 400, None)
else:
encodedPassword = request.json['password'].encode('utf-8')
hashed = bcrypt.hashpw(
encodedPassword, bcrypt.gensalt(app.bcrypt_rounds))
request.json['password'] = hashed
user_collection.insert_one(request.json)
#requires_auth
def get(self):
return (None, 200, None)
api.add_resource(User, '/eventure/api/v1.1/user/')
# Must define a custom JSON Serializer for flask_restful
# this is because ObjectId is not a string, and therefore,
# Flask's default serializer cannot serialize it.
#api.representation('application/json')
def output_json(data, code, headers=None):
resp = make_response(JSONEncoder().encode(data), code)
resp.headers.extend(headers or {})
return resp
if __name__ == '__main__':
app.config['TRAP_BAD_REQUEST_ERRORS'] = True
app.run(host='localhost', port=8789, debug=True)
And this is my register function in swift:
#IBAction func register(_ sender: AnyObject) {
let url = URL(string: "http://localhost:8789/eventure/api/v1.1/user/")
var request = URLRequest(url: url!)
request.httpMethod = "POST"
request.setValue(generateBasicAuthHeader(username: username.text!, password: password.text!), forHTTPHeaderField: "Authorization")
let session = URLSession.shared
let task = session.dataTask(with: request) { data, response, error in
if let response = response, let data = data {
print(String(data: data, encoding: String.Encoding.utf8))
}
}
task.resume()
self.username.text = ""
self.password.text = ""
}
traceback:
[28/Oct/2016 19:22:33] "POST /eventure/api/v1.1/user/ HTTP/1.1" 500 -
Traceback (most recent call last):
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1836, in __call__
return self.wsgi_app(environ, start_response)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1820, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask_restful/__init__.py", line 270, in error_router
return original_handler(e)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1403, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/_compat.py", line 32, in reraise
raise value.with_traceback(tb)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1817, in wsgi_app
response = self.full_dispatch_request()
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1477, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask_restful/__init__.py", line 270, in error_router
return original_handler(e)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1381, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/_compat.py", line 32, in reraise
raise value.with_traceback(tb)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1475, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1461, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask_restful/__init__.py", line 471, in wrapper
resp = resource(*args, **kwargs)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/views.py", line 84, in view
return self.dispatch_request(*args, **kwargs)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask_restful/__init__.py", line 581, in dispatch_request
resp = meth(*args, **kwargs)
File "/Users/Dynee/eventure-backend-api/server.py", line 128, in post
if (request.json['username'] == None
TypeError: 'NoneType' object is not subscriptable
Also here is the generateBasicAuthHeader function:
func generateBasicAuthHeader(username: String, password: String) -> String {
let loginString = String(format: "%#:%#", username, password)
let loginData = loginString.data(using: String.Encoding.utf8)!
let base64LoginString = loginData.base64EncodedString()
let basicAuthHeader = "Basic \(base64LoginString)"
return basicAuthHeader
}
You need to explicitly set the content-type to application/json for request.json to work properly in flask. If the header isn't set, request.json would return None.
But the recommended to get json data in flask from a post request is to use request.get_json()
I'll also urge you to test your api with the nifty requests module before using your ios application.
>>> import requests
>>> requests.post(url, json={'name': 'hello world'})
It already sets the appropriate headers required to make a json request
If it works with the requests module, then you can be sure that it's going to work with your ios application. you just need to make sure you're setting the correct content-type.
You can forcefully tell flask to ignore the content-type header with
request.get_json(force=True)

error with flask session during youtube api v3 authentication [duplicate]

This question already has answers here:
demystify Flask app.secret_key
(2 answers)
Closed 7 years ago.
I'm developing an app in python that uses YT API. I decided to move it to the web and i'm using Flask. I took the auth example from Google guides. For testing purposes i'm trying to create a playlist after authentication. It looks like this:
#app.route('/')
def index():
if 'credentials' not in flask.session:
return flask.redirect(flask.url_for('oauth2callback'))
credentials = client.OAuth2Credentials.from_json(flask.session['credentials'])
if credentials.access_token_expired:
return flask.redirect(flask.url_for('oauth2callback'))
else:
http_auth = credentials.authorize(httplib2.Http())
yt_service = discovery.build('youtube', 'v3', http_auth)
playlists_insert_response = yt_service.playlists().insert(
part="snippet,status",
body=dict(
snippet=dict(
title="Test Playlist",
description="A private playlist created with the YouTube API v3"
),
status=dict(
privacyStatus="private"
)
)
).execute()
return playlists_insert_response["id"]
#app.route('/oauth2callback')
def oauth2callback():
flow = client.flow_from_clientsecrets('youtube/client_secret.json',scope='https://www.googleapis.com/auth/youtube',redirect_uri=flask.url_for('oauth2callback', _external=True))
if 'code' not in flask.request.args:
auth_uri = flow.step1_get_authorize_url()
return flask.redirect(auth_uri)
else:
auth_code = flask.request.args.get('code')
credentials = flow.step2_exchange(auth_code)
flask.session['credentials'] = credentials.to_json()
return flask.redirect(flask.url_for('index'))
So in browser I authenticate, accept app's permisions and then I see 500 internal error with ouath2callback URI and code parameter. In the error log I see this:
2015-09-07 10:23:08,319 :Successfully retrieved access token
2015-09-07 10:23:08,330 :Exception on /oauth2callback [GET]
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1687, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1360, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1358, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1344, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/home/Kraxi/youtube/playlist.py", line 63, in oauth2callback
flask.session['credentials'] = credentials.to_json()
File "/usr/local/lib/python2.7/dist-packages/werkzeug/local.py", line 339, in __setitem__
self._get_current_object()[key] = value
File "/usr/local/lib/python2.7/dist-packages/flask/sessions.py", line 57, 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.
I tihnk it might be something wrong with Flask session - maybe seassion type?
Using Flask 0.10
Any ideas, please?
In order to use sessions you need to set a SECRET_KEY.
Extracted from the docs:
# set the secret key. keep this really secret:
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'

Python Flask SQLalchemy JSON POST Error

I'm trying to post the following JSON and save to a MySQL database on a Flask server and Python 2.7, restless framework and SQLAlchemy with curl:
curl -i -H "Accept: application/json" -X POST -d '{"attribute_id": "1", "product_id": "44","text":"Something","language":"1"}' http://seroney-pc:5000/api/attributes
{
"attribute_id": "1",
"product_id": "44",
"text": "Something",
"language": "1"
}
My code is as follows:
from flask import Flask,request,jsonify, abort
from flask_sqlalchemy import SQLAlchemy
import flask_restless
app = Flask(__name__)
db = SQLAlchemy(app)
manager = flask_restless.APIManager(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:seroney#localhost:3306/test'
class Attributes(db.Model):
__tablename__ = 'oc_product_attribute'
product_id = db.Column(db.Integer,primary_key=True)
attribute_id = db.Column(db.Integer,primary_key=True)
language_id = db.Column(db.Integer,primary_key=True)
text=db.Column(db.String)
#app.route('/api/attributes/',methods=['GET'])
def getProductAttributes():
if request.method =='GET':
results = Attributes.query.limit(10).offset(0).all()
json_results = []
for result in results:
d = {
'product_id':result.product_id,
'attribute_id':result.attribute_id,
'language_id':result.language_id,
'text':result.text
}
json_results.append(d)
return jsonify(items = json_results)
#app.route('/api/attributes/', methods=['POST'])
def postProductAttributes():
product_id = request.json['product_id']
attribute_id = request.json['attribute_id']
language_id = request.json['language_id']
text = request.json['text']
if product_id is None or attribute_id is None or language_id is None or text is None:
return jsonify({"message": "Error."}), 400
new_attrib = (product_id,attribute_id,language_id,text)
db.session.add(new_attrib)
db.session.commit()
return jsonify({'message' :'Attribute Created successfully'}), 200
if __name__ == '__main__':
app.run(debug=True)
When I POST I keep I getting an Internal Server Error. Any help is highly appreciated.
The traceback is:
seroney-pc - - [23/Dec/2014 20:48:40] "POST /api/attributes HTTP/1.1" 500 -
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1836, in __call__
return self.wsgi_app(environ, start_response)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1820, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1403, in handle_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1817, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1477, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1381, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1475, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1453, in dispatch_request
self.raise_routing_exception(req)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1439, in raise_routing_exception
raise FormDataRoutingRedirect(request)
Note: this exception is only raised in debug mode
You are posting to the URL without a / at the end, but you specified your route with a trailing /. When you do that, Flask issues a redirect to the 'canonical' URL, with the /.
Because you are using POST, the post data will be lost, so in debug mode an exception is raised instead to inform you that you should use the trailing slash in your post instead.
Had you looked at the body of the error message, you'd have seen something like:
FormDataRoutingRedirect: A request was sent to this URL (http&colon;//seroney-pc:5000/api/attributes) but a redirect was
issued automatically by the routing system to "http&colon;//seroney-pc:5000/api/attributes/". The URL was defined with a trailing slash so Flask will automatically redirect to the URL with the trailing slash if it was accessed without one. Make sure to directly send your POST-request to this URL since we can't make browsers or HTTP clients redirect with form data reliably or without user interaction.
Note: this exception is only raised in debug mode
See the Rule Format documentation:
URL rules that end with a slash are branch URLs, others are leaves. If you have strict_slashes enabled (which is the default), all branch URLs that are visited without a trailing slash will trigger a redirect to the same URL with that slash appended.
Note that your curl POST uses the wrong header; you need to set the Content-Type header. Your view is looking for the language_id key, but your post contains only a language key, you need to correct that too:
curl -i -H "Content-Type: application/json" -X POST \
-d '{"attribute_id": "1", "product_id": "44","text":"Something","language_id":"1"}' http://seroney-pc:5000/api/attributes/
The Accept header may be useful too, but it is used for negotiating the response content type, and you have your views hardcoded to return JSON.
Your code creating the database object is also incorrect, you need to call the model and pass in the arguments as separate arguments, then pass in the resulting to session.add():
new_attrib = Attributes(*new_attrib)
db.session.add(new_attrib)
but just reusing the JSON object would be easier here:
db.session.add(Attributes(**request.json))

Categories