I am using rest client in my mozilla browser to call an auth service.
When i pass my credentials in Body, i get an "auth-token" . I then set this token in the header in the browser HEADERS tab.
I have to parse this header which i am setting in the browser in my python script as a variable. Further, after getting this value in my script i have to authenticate the token for its validity.
However i am unable to get the tokens value in my script. My auth function is ready. I just have to fetch the token
How should i fetch this token value from the header ??
Code:
def check_authentication(auth):
print "Auth" , auth
chek_auth_url = ("http://10.168.2.161/auth/v/%s" % (auth))
auth = requests.get(chek_auth_url)
if auth.status_code == 200:
return True
I have to pass the token as a paramter in this function and call in this function in main for authentication.
def crossdomain(origin=None, methods=None, headers=None, max_age=21600, attach_to_all=True, automatic_options=True):
if methods is not None:
methods = ', '.join(sorted(x.upper() for x in methods))
if headers is not None and not isinstance(headers, basestring):
headers = ', '.join(x.upper() for x in headers)
if not isinstance(origin, basestring):
origin = ', '.join(origin)
if isinstance(max_age, timedelta):
max_age = max_age.total_seconds()
def get_methods():
if methods is not None:
return methods
options_resp = current_app.make_default_options_response()
return options_resp.headers['allow']
def decorator(f):
def wrapped_function(*args, **kwargs):
if automatic_options and request.method == 'OPTIONS':
resp = current_app.make_default_options_response()
else:
resp = make_response(f(*args, **kwargs))
if not attach_to_all and request.method != 'OPTIONS':
return resp
h = resp.headers
h['Access-Control-Allow-Origin'] = origin
h['Access-Control-Allow-Methods'] = get_methods()
h['Access-Control-Max-Age'] = str(max_age)
if headers is not None:
h['Access-Control-Allow-Headers'] = headers
#h['Access-Control-Allow-Headers'] = "Content-Type"
return resp
f.provide_automatic_options = False
return update_wrapper(wrapped_function, f)
return decorator
#app.route('/test', methods=['POST', 'OPTIONS'])
#crossdomain(origin='*', headers='Content-Type')
def get_storage():
*check_authentication is called here and token is passed as a parameter*
*if token is valid further task i hav to do*
if __name__ == '__main__':
app.run(host='192.168.56.1', port=8080, threaded=True)
Self-Help is the best help..
Finally i found a fix:
The token value is fetched in the variable tokenValue. I can now do my further coding.
tokenValue = request.headers.get("token")
if tokenValue == None:
return "x-auth-token not passed in header, please pass the token."
else:
print "Token passed is", tokenValue
Related
So I have this endpoint below(Block 2) called from the directly below(Block 1)...the headers I see in the browser appear to be sending the access_token_cookie in the cookie header....I also have a after_header block(Block 3)
THE AXIOS CALL FROM THE PAGE
var myFunction = function(element){
console.log(element.innerText);
axios.defaults.withCredentials = true;
axios.post('/api/cmsaudit/'+element.innerText).then(function(response){
//console.log(response.data);
document.getElementById('resultcol').innerHTML = response.data;
}).catch(function(error){
})
THE AJAX ENDPOINT CALL
#cms_blueprint.route('/api/cmsaudit/<cause>',methods=['POST','GET'])
#jwt_required() <---- if I remove this it works fine.
def applist(cause):
f = open('cause.txt','w')
f.write('inhere')
f.write(str(datetime.now()))
getapps = OracleConnect()
opencon = getapps.openconnection()
if opencon == 0:
if cause == 'List Applications':
getapps.mycursor.execute("QUERY")
result = getapps.mycursor.fetchall()
getapps.closeconnection
print(result)
results = {}
results["value"]=[]
for x in result:
results["value"].append(x[0])
sys.stdout.flush()
return render_template('cmsaudit/applist.html',applist=results["value"])
return jsonify(results),200
else:
f.write('else is running')
return jsonify({'msg':str(opencon)})
AFTER REQUEST BLOCK TO CHECK/REFRESH TOKEN
#app.after_request
def refresh_expiring_jwts(response):
try:
now = datetime.now()
if request.path == '/api/jwt/check':
return response
exp_timestamp = get_jwt()["exp"]
target_timestamp = datetime.timestamp(now + timedelta(minutes=28))
if target_timestamp > exp_timestamp:
access_token = create_access_token(identity=get_jwt_identity())
set_access_cookies(response, access_token)
return response
except (RuntimeError, KeyError):
# Case where there is not a valid JWT. Just return the original respone
return response
Recently, I have started flask. I found flask-sqlalchemy to work with flask. So I was using this. But I faced some problems. I am working on repl.it. When the repl goes assleep, I tried to use with the flask enabled website. But saw that no datas were showing. But before that I added a lot of datas. I can't figure out what is wrong but could you help me?
It may help you:
from flask_sqlalchemy import SQLAlchemy
import re
from flask import Flask, abort, jsonify, redirect, request
import os
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite3'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
class apikey(db.Model):
apikey = db.Column(db.String(25), primary_key=True, unique = True)
def __init__(self, apikey):
self.apikey = apikey
class shorturl(db.Model):
short_query = db.Column(db.String(15), primary_key=True, unique = True)
original = db.Column(db.String(1000))
visits = db.Column(db.Integer)
def __init__(self, short_query,original, visits):
self.short_query = short_query
self.original = original
self.visits = visits
def url_valid(url):
return re.match(regex, url) is not None
def bad_request(message):
response = jsonify({'message': message})
response.status_code = 400
return response
def errreq(message):
response = jsonify({'message': message})
response.status_code = 404
return response
#app.route('/')
def show_all():
return redirect("https://www.cburst.ml", code=301)
#app.route('/addapi', methods=['GET'])
def addapi():
if request.args.get('apikey') is not None:
api_key = request.args.get('apikey')
apiadd = apikey(apikey=api_key)
db.session.add(apiadd)
db.session.commit()
return jsonify({'message':"Done"}), 200
else:
return bad_request("Nothing Provided")
#app.route('/add', methods=['POST'])
def add():
if not request.json:
return bad_request('Url must be provided in json format.')
if "original" not in request.json :
return bad_request('Url parameter not found.')
if "short_query" not in request.json:
return bad_request('Url parameter not found.')
original = request.json['original']
short = request.json['short_query']
if shorturl.query.filter_by(short_query = short).first() is not None:
return bad_request("Already Exists")
visits = 0
if original[:4] != 'http':
original = 'http://' + original
if not url_valid(original):
return bad_request('Provided url is not valid.')
url_db = shorturl(
short_query=short, original=original, visits=visits)
shortened_url = short
db.session.add(url_db)
db.session.commit()
return jsonify({'link': shortened_url}), 201
#app.route('/add', methods=['GET'])
def add_get():
if request.args.get('apikey') is not None and request.args.get('original') is not None and request.args.get('short_query') is not None:
api_key = request.args.get('apikey')
original = request.args.get('original')
short_query = request.args.get('short_query')
if apikey.query.filter_by(apikey = api_key).first() is None:
return errreq("Invalid API Key")
else:
if shorturl.query.filter_by(short_query=short_query).first() is not None:
return errreq("Already Exists")
else:
if original[:4] != 'http':
original = 'http://' + original
if not url_valid(original):
return bad_request('Provided url is not valid.')
url_db = shorturl(
short_query=short_query, original=original, visits=0)
db.session.add(url_db)
db.session.commit()
return jsonify({'link': short_query}), 200
else:
return bad_request("Nothing Provided")
#app.route('/view/<alias>', methods=['GET'])
def get_viewcount(alias):
if shorturl.query.filter_by(short_query=alias).first() is None:
return bad_request('Unknown alias.')
else:
return jsonify({'visits':shorturl.query.filter_by(short_query=alias).first().visits}),200
#app.route('/<alias>', methods=['GET'])
def get_shortened(alias):
if shorturl.query.filter_by(short_query=alias).first() is None:
return bad_request('Unknown alias.')
visits = shorturl.query.filter_by(short_query=alias).first().visits
url_db = shorturl.query.filter_by(short_query=alias).first()
url_db.visits = visits + 1
db.session.commit()
url = shorturl.query.filter_by(short_query=alias).first().original
return redirect(url, code=302)
# From https://stackoverflow.com/questions/7160737/python-how-to-validate-a-url-in-python-malformed-or-not#7160778
# Slightly modified to not use ftp.
regex = re.compile(
r'^(?:http)s?://'
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|'
r'localhost|'
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
r'(?::\d+)?'
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
if __name__ == '__main__':
'''
if os.path.exists('./db.sqlite3'):
app.run(host="0.0.0.0", port=8000, debug=False)
else: '''
db.create_all()
app.run(host="0.0.0.0", port=8000, debug=False)
Here if I add api key in /appapi with key in query string, I can add the key in the database. But the key added in the database does not work after a couple of hours.
Thanks in advance.
I figured this out. It is because I have added db.create_all() inside the main function. Removing this just works.
Could someone demonstrate writing a file to IPFS via the HTTP API (/files/write) and Python?
My code is getting messier every time I modify it.
https://pastebin.com/W9eNz1Pb
def api(*argv, **kwargs):
url = "http://127.0.0.1:5001/api/v0/"
for arg in argv:
arg = arg.replace(" ", "/")
if arg[:-1] != "/":
arg += "/"
url += arg
url = url[0:-1]
if kwargs:
url+="?"
for val in kwargs:
if val != "post":
url = url + val + "=" + kwargs[val] + "&"
url = url[0:-1]
print(url)
try:
if "post" in kwargs:
print("POST DATA")
with urllib.request.urlopen(url=url, data=urllib.parse.urlencode(kwargs["post"]).encode("ascii")) as response:
return response.read()
else:
with urllib.request.urlopen(url, timeout=300) as response:
return response.read()
except:
return b"""{"ERROR": "CANNOT CONNECT TO IPFS!"}"""
class file(object):
def __init__(self, p):
self.p = p
if self.p[0] != "/":
self.p = "/" + self.p
def read(self):
return api("files", "read", arg=self.p).decode()
def write(self, s, *argv):
if argv:
return api("files", "write", arg=self.p, offset=str(argv[0]), create="True", parents="True", post={"Data": s})
else:
return api("files", "write", arg=self.p, truncate="True", create="True", parents="True", post={"Data": s})
file.read() works perfectly. But file.write() is being a pain in the rear.
Here's a minimal example to write a file via the /files/write HTTP API in Python:
import requests, urllib
NODE = "http://localhost:5001"
FILE_PATH = "./example" # path to file you're trying to add
MFS_PATH = "/example" # mfs path you're trying to write to
response = requests.post(NODE+"/api/v0/files/write?arg=%s&create=true" % urllib.parse.quote(MFS_PATH), files={FILE_PATH:open(FILE_PATH, 'rb')})
make sure ipfs daemon is running
ipfs init
ipfs daemon
your url endpoint is wrong. If you check documentaion, for adding file, url should be
url = "http://127.0.0.1:5001/api/v0/add"
create a function to upload so u can use this logic in other parts of your project:
def add_to_ipfs(filepath):
from pathlib import Path
import requests
# rb means open in binary. read binary
with Path(filepath).open("rb") as fp:
image_binary=fp.read()
# we need to make post request to this endpoint.
url = "http://127.0.0.1:5001/api/v0/add"
# check the response object
response = requests.post(url, files={"file": image_binary})
ipfs_hash=response.json()["Hash"]
# "./img/myImage.png" -> "myImage.png" split by "/" into array, take the last element
filename=filepath.split("/")[-1:][0]
image_uri=f"https://ipfs.io/ipfs/{ipfs_hash}?filename={filename}"
print("image uri on ipfs",image_uri)
return image_uri
this is the Response type from ipfs
{
"Bytes": "<int64>",
"Hash": "<string>",
"Name": "<string>",
"Size": "<string>"
}
I have a working application that accepts SOAP requests, processes the requests, forwards the SOAP request to an API, processes the response, and then forwards the response to the client.
I'm trying to change this application so that it will be JSON between my application and the client but still use SOAP between API and my application
Now, it can successfully accept JSON requests from client and send/receive SOAP with API. However, all the responses to client are empty.
The only case that I receive a non-empty response is when there are validation errors with my JSON request.
Here are some code that might be relevant
app = Application([MyServer],
MY_NAMESPACE,
in_protocol=JsonDocument(validator='soft'),
out_protocol=JsonDocument())
application_server = csrf_exempt(MyDjangoApplication(app))
definition of MyDjangoApplication
class MyDjangoApplication(DjangoApplication):
def __call__(self, request, *args, **kwargs):
retval = self.HttpResponseObject()
def start_response(status, headers):
# Status is one of spyne.const.http
status, reason = status.split(' ', 1)
retval.status_code = int(status)
for header, value in headers:
retval[header] = value
environ = request.META.copy()
if request.method == 'POST':
response = self.handle_rpc(environ, start_response)
else:
home_path = reverse('proxy:list_method')
uri = MY_ENDPOINT_URL or request.build_absolute_uri(home_path)
# to generate wsdl content
response = self._WsgiApplication__handle_wsdl_request(environ, start_response, uri)
if request.path == home_path and _is_wsdl_request(environ):
fn = None
elif 'method_name' in kwargs:
fn = view_method
else:
fn = list_method
if fn:
return fn(request, app=self, *args, **kwargs)
self.set_response(retval, response)
return retval
Definition of MyServer
class MyServer(ServiceBase):
#rpc(MyTestMethodRequest, Sign, **method(_returns=MyTestMethodResponse))
#check_method()
def TestMethod(ctx, request, signature):
response = {
'Data': "test"
}
return response
Definitions of MyTestMethodRequest, MyTestMethodResponse:
class MyTestMethodRequest(ComplexModel):
__namespace__ = MY_NAMESPACE
MyString = String(encoding=STR_ENCODING)
class MyTestMethodResponse(ComplexModel):
__namespace__ = MY_NAMESPACE
Data = String(encoding=STR_ENCODING)
Definition of check_method:
def check_method(error_handler=None):
def _check_method(func):
method_name = func.__name__
def __check_method(ctx, request, signature, *args, **kwargs):
if hasattr(request, '__dict__'):
request = request.__dict__
if hasattr(signature, '__dict__'):
signature = signature.__dict__
response = func(ctx, request or {}, signature or {}, *args, **kwargs)
# setting output protocol
output_message = generate_out_string(ctx, [response])
return response
__check_method.__name__ = method_name
__check_method.__doc__ = func.__doc__
return __check_method
return _check_method
Definition of generate_out_string:
def generate_out_string(ctx, objects):
ctx.out_protocol = ctx.in_protocol
return _generate_out_string(ctx, objects)
def _generate_out_string(ctx, objects):
protocol = ctx.out_protocol
ctx.out_object = objects
protocol.serialize(ctx, protocol.RESPONSE)
protocol.create_out_string(ctx)
out_string = list(ctx.out_string)
return out_string[0] if out_string else ''
Note: Most of these definitions have been simplified (I have removed lines which I think are not relevant)
Looking at the code you posted, I can't say I understand what good all those additional decorators and modifiers around arguments do.
Removing them should fix all of your problems.
So let:
class MyTestMethodRequest(ComplexModel):
__namespace__ = MY_NAMESPACE
MyString = Unicode
class MyTestMethodResponse(ComplexModel):
__namespace__ = MY_NAMESPACE
Data = Unicode
Assuming you have the following service:
class MyService(ServiceBase):
#rpc(MyTestMethodRequest, Sign, _returns=MyTestMethodResponse)
def TestMethod(ctx, request, signature):
return MyTestMethodResponse(data="test")
You can have:
app_json = Application([MyService],
MY_NAMESPACE,
in_protocol=JsonDocument(validator='soft'),
out_protocol=JsonDocument())
and
app_soap = Application([MyService],
MY_NAMESPACE,
in_protocol=Soap11(validator='lxml'),
out_protocol=Soap11())
which in turn you can pass to DjangoApplication as usual.:
app_json_dja = csrf_exempt(DjangoApplication(app_json))
app_soap_dja = csrf_exempt(DjangoApplication(app_soap))
which in turn you can mount in Django's url router.
I hope this helps!
I saw that OAuth is recommended also for facebook canvas apps and the code I had didn't work, it tried to use the signed_request but every time i changed the session I had to do a reload:
class Facebook(object):
"""Wraps the Facebook specific logic"""
def __init__(self, app_id=conf.FACEBOOK_APP_ID,
app_secret=conf.FACEBOOK_APP_SECRET):
self.app_id = app_id
self.app_secret = app_secret
self.user_id = None
self.access_token = None
self.signed_request = {}
def api(self, path, params=None, method=u'GET', domain=u'graph'):
"""Make API calls"""
if not params:
params = {}
params[u'method'] = method
if u'access_token' not in params and self.access_token:
params[u'access_token'] = self.access_token
result = json.loads(urlfetch.fetch(
url=u'https://' + domain + u'.facebook.com' + path,
payload=urllib.urlencode(params),
method=urlfetch.POST,
headers={
u'Content-Type': u'application/x-www-form-urlencoded'})
.content)
if isinstance(result, dict) and u'error' in result:
raise FacebookApiError(result)
return result
def load_signed_request(self, signed_request):
"""Load the user state from a signed_request value"""
try:
sig, payload = signed_request.split(u'.', 1)
sig = self.base64_url_decode(sig)
data = json.loads(self.base64_url_decode(payload))
expected_sig = hmac.new(
self.app_secret, msg=payload, digestmod=hashlib.sha256).digest()
# allow the signed_request to function for upto 1 day
if sig == expected_sig and \
data[u'issued_at'] > (time.time() - 86400):
self.signed_request = data
self.user_id = data.get(u'user_id')
self.access_token = data.get(u'oauth_token')
except ValueError, ex:
pass # ignore if can't split on dot
#property
def user_cookie(self):
"""Generate a signed_request value based on current state"""
if not self.user_id:
return
payload = self.base64_url_encode(json.dumps({
u'user_id': self.user_id,
u'issued_at': str(int(time.time())),
}))
sig = self.base64_url_encode(hmac.new(
self.app_secret, msg=payload, digestmod=hashlib.sha256).digest())
return sig + '.' + payload
#staticmethod
def base64_url_decode(data):
data = data.encode(u'ascii')
data += '=' * (4 - (len(data) % 4))
return base64.urlsafe_b64decode(data)
#staticmethod
def base64_url_encode(data):
return base64.urlsafe_b64encode(data).rstrip('=')
class CsrfException(Exception):
pass
class BaseHandler(webapp.RequestHandler):
facebook = None
user = None
csrf_protect = True
def initialize(self, request, response):
"""General initialization for every request"""
super(BaseHandler, self).initialize(request, response)
try:
self.init_facebook()
self.init_csrf()
self.response.headers[u'P3P'] = u'CP=HONK' # iframe cookies in IE
except Exception, ex:
self.log_exception(ex)
raise
def handle_exception(self, ex, debug_mode):
"""Invoked for unhandled exceptions by webapp"""
self.log_exception(ex)
self.render(u'error',
trace=traceback.format_exc(), debug_mode=debug_mode)
def log_exception(self, ex):
"""Internal logging handler to reduce some App Engine noise in errors"""
msg = ((str(ex) or ex.__class__.__name__) +
u': \n' + traceback.format_exc())
if isinstance(ex, urlfetch.DownloadError) or \
isinstance(ex, DeadlineExceededError) or \
isinstance(ex, CsrfException) or \
isinstance(ex, taskqueue.TransientError):
logging.warn(msg)
else:
logging.error(msg)
def set_cookie(self, name, value, expires=None):
"""Set a cookie"""
if value is None:
value = 'deleted'
expires = datetime.timedelta(minutes=-50000)
jar = Cookie.SimpleCookie()
jar[name] = value
jar[name]['path'] = u'/'
if expires:
if isinstance(expires, datetime.timedelta):
expires = datetime.datetime.now() + expires
if isinstance(expires, datetime.datetime):
expires = expires.strftime('%a, %d %b %Y %H:%M:%S')
jar[name]['expires'] = expires
self.response.headers.add_header(*jar.output().split(u': ', 1))
def render(self, name, **data):
"""Render a template"""
if not data:
data = {}
data[u'js_conf'] = json.dumps({
u'appId': conf.FACEBOOK_APP_ID,
u'canvasName': conf.FACEBOOK_CANVAS_NAME,
u'userIdOnServer': self.user.user_id if self.user else None,
})
data[u'logged_in_user'] = self.user
data[u'message'] = self.get_message()
data[u'csrf_token'] = self.csrf_token
data[u'canvas_name'] = conf.FACEBOOK_CANVAS_NAME
self.response.out.write(template.render(
os.path.join(
os.path.dirname(__file__), 'templates', name + '.html'),
data))
def init_facebook(self):
"""Sets up the request specific Facebook and User instance"""
facebook = Facebook()
user = None
# initial facebook request comes in as a POST with a signed_request
if u'signed_request' in self.request.POST:
facebook.load_signed_request(self.request.get('signed_request'))
# we reset the method to GET because a request from facebook with a
# signed_request uses POST for security reasons, despite it
# actually being a GET. in webapp causes loss of request.POST data.
self.request.method = u'GET'
self.set_cookie(
'u', facebook.user_cookie, datetime.timedelta(minutes=1440))
elif 'u' in self.request.cookies:
facebook.load_signed_request(self.request.cookies.get('u'))
# try to load or create a user object
if facebook.user_id:
user = User.get_by_key_name(facebook.user_id)
if user:
# update stored access_token
if facebook.access_token and \
facebook.access_token != user.access_token:
user.access_token = facebook.access_token
user.put()
# refresh data if we failed in doing so after a realtime ping
if user.dirty:
user.refresh_data()
# restore stored access_token if necessary
if not facebook.access_token:
facebook.access_token = user.access_token
if not user and facebook.access_token:
me = facebook.api(u'/me', {u'fields': _USER_FIELDS})
try:
friends = [user[u'id'] for user in me[u'friends'][u'data']]
user = User(key_name=facebook.user_id,
user_id=facebook.user_id, friends=friends,
access_token=facebook.access_token, name=me[u'name'],
email=me.get(u'email'), picture=me[u'picture'])
user.put()
except KeyError, ex:
pass # ignore if can't get the minimum fields
self.facebook = facebook
self.user = user
def init_csrf(self):
"""Issue and handle CSRF token as necessary"""
self.csrf_token = self.request.cookies.get(u'c')
if not self.csrf_token:
self.csrf_token = str(uuid4())[:8]
self.set_cookie('c', self.csrf_token)
if self.request.method == u'POST' and self.csrf_protect and \
self.csrf_token != self.request.POST.get(u'_csrf_token'):
raise CsrfException(u'Missing or invalid CSRF token.')
def set_message(self, **obj):
"""Simple message support"""
self.set_cookie('m', base64.b64encode(json.dumps(obj)) if obj else None)
def get_message(self):
"""Get and clear the current message"""
message = self.request.cookies.get(u'm')
if message:
self.set_message() # clear the current cookie
return json.loads(base64.b64decode(message))
I changed the code above to OAuth serverside for the canvas app and then I could make the app behave like I wanted to. But if I use OAuth 2.0 do I really need the signed_request? If I use OAuth the signed_request seems unneccessary and OAuth can do it all. I changed the function init_facebookrather much:
def init_facebook(self):
facebook = Facebook()
user = None
# initial facebook request comes in as a POST with a signed_request
if 'signed_request' in self.request.POST:
fbdata= parse_signed_request(self.request.get('signed_request'), facebookconf.FACEBOOK_APP_SECRET)
facebook.signed_request = fbdata
facebook.user_id = fbdata.get('user_id')
facebook.access_token = fbdata.get('oauth_token')
if facebook.user_id:
graph = GraphAPI(facebook.access_token)
user = graph.get_object("me") #write the access_token to the datastore
fbuser = FBUser.get_by_key_name(user["id"])
#logging.debug("fbuser "+fbuser.name)
self.user = fbuser
if not fbuser:
fbuser = FBUser(key_name=str(user["id"]),
id=str(user["id"]),
name=user["name"],
profile_url=user["link"],
access_token=facebook.access_token)
fbuser.put()
elif fbuser.access_token != facebook.access_token:
fbuser.access_token = facebook.access_token
fbuser.put()
# try to load or create a user object
if facebook.user_id:
logging.debug("loading facebook.user_id")
user = FBUser.get_by_key_name(facebook.user_id)
if user:
# update stored access_token
if facebook.access_token and \
facebook.access_token != user.access_token:
user.access_token = facebook.access_token
user.put()
# refresh data if we failed in doing so after a realtime ping
if user.dirty:
user.refresh_data()
# restore stored access_token if necessary
if not facebook.access_token:
facebook.access_token = user.access_token
if not user and facebook.access_token:
me = facebook.api('/me', {'fields': _USER_FIELDS})
try:
friends = [user['id'] for user in me['friends']['data']]
user = FBUser(key_name=facebook.user_id,
id=facebook.user_id, friends=friends,
access_token=facebook.access_token, name=me['name'],
email=me.get('email'), picture=me['picture'])
user.put()
except KeyError, ex:
pass # ignore if can't get the minimum fields
self.facebook = facebook
self.user = user
Now it uses OAuth instead of changing the method from POST to GET which I never understodd why it had to do anyway. I have more code but maybe you know enough already to tell me if I'm doing this wrong and should go back to a more basic example.
For example I had some problems logging out the user and I had to write a custom logout handler:
class FBLogoutHandler(webapp2.RequestHandler):
def get(self):
logging.debug('in fblogout')
current_user = main.get_user_from_cookie(self.request.cookies, facebookconf.FACEBOOK_APP_ID, facebookconf.FACEBOOK_APP_SECRET)
if current_user:
graph = main.GraphAPI(current_user["access_token"])
profile = graph.get_object("me")
accessed_token = current_user["access_token"]
logging.debug('setting cookie')
self.set_cookie("fbsr_" + facebookconf.FACEBOOK_APP_ID, None, expires=time.time() - 86400)
logging.debug('redirecting with token '+str(accessed_token))
self.redirect('https://www.facebook.com/logout.php?next=http://www.facebook.com&access_token=%s' % accessed_token)
def set_cookie(self, name, value, expires=None):
if value is None:
value = 'deleted'
expires = datetime.timedelta(minutes=-50000)
jar = Cookie.SimpleCookie()
jar[name] = value
jar[name]['path'] = '/'
if expires:
if isinstance(expires, datetime.timedelta):
expires = datetime.datetime.now() + expires
if isinstance(expires, datetime.datetime):
expires = expires.strftime('%a, %d %b %Y %H:%M:%S')
jar[name]['expires'] = expires
self.response.headers.add_header(*jar.output().split(': ', 1))
def get_host(self):
return os.environ.get('HTTP_HOST', os.environ['SERVER_NAME'])
This solution might not be best so I wonder what alternatives there are for my canvas app?
Thanks
The signed_request is always updated when you access a canvas app (when refreshing the parent frame), so it's a good way of getting the latest access_token for the user (which usually only lasts for a hour).
The access_token obtained using oauth has the same expiration time, so if you rely solely on oauth, you will have to authenticate the user ever hour.
I tend to use a combination of the two. On initial use of the app, I use oauth to get the access_token and access the API. Then, on subsequent visits, I rely on the signed_request to get the access_token and automatically personalise the content for the user (since it tell me who they are and gives me access to the API without following the OAuth flow again).