Authlib - passing authorize url as json - flask

I'm building a SPA using Flask as an api and Vue js as front end. I'm using Authlib for user authentication and planning to use Facebook OAuth. I've already tried the example with Facebook and it's working. But I want to build this in a RESTful way. What I'm trying to do is change this part from app.py from the example:
#app.route('/login')
def login():
redirect_uri = url_for('auth', _external=True)
return oauth.google.authorize_redirect(redirect_uri)
to a json.
Is there a method in the library to get the url of Facebook dialog so that I can pass that as json and do the redirection in Vue?

Do you mean that you want to return a url value in the JSON response?
resp = oauth.google.authorize_redirect(redirect_uri)
url = resp.location
return jsonify(url=url)
Like the above code?

Per the documentation:
>>> import requests_oauthlib
>>> from requests_oauthlib import OAuth2Session
>>> from requests_oauthlib.compliance_fixes import facebook_compliance_fix
>>> facebook = OAuth2Session(client_id='test', redirect_uri='https://test.me')
>>> facebook = facebook_compliance_fix(facebook)
>>> authorization_url, state = facebook.authorization_url('https://www.facebook.com/dialog/oauth')
>>> authorization_url
'https://www.facebook.com/dialog/oauth?response_type=code&client_id=test&redirect_uri=https%3A%2F%2Ftest.me&state=bfipsir5GsKc1CbdPZCgBT0jIn2eq6'

Related

Good way/place to authenticate Keystone/Openstack API from Django

This is my first post on Stackoverflow and I'm new to Django, I hope you'll understand.
I want to use Django to provide a portal with authentication, which will have to consume an Keystone/Openstack API, to create/delete Projects, grant/remove rights.
Openstack provides a RestFul API, on which I have to authenticate (I provide credentials, and receive back a token).
I have 2 possibilities to access this API:
Using python client: python-keystoneclient
Using directly the restfulAPI
Nevermind the option 1 or 2, I'm able to login and interact with the API, I do this in the view.
My problem is, each time I change the page/view, I have to authenticate again. I don't know how to use/share the "session or client object" in other views.
>>> from keystoneauth1.identity import v3
>>> from keystoneauth1 import session
>>> from keystoneclient.v3 import client
>>> auth = v3.Password(auth_url='https://my.keystone.com:5000/v3',
... user_id='myuserid',
... password='mypassword',
... project_id='myprojectid')
>>> sess = session.Session(auth=auth)
>>> keystone = client.Client(session=sess, include_metadata=True)
I tried to pass the object as a session variable with request.session and request.session.get, but the object is not serializable. I serialized it, but I can't use it on the other view.
Maybe I shouldn't access the API in the view?
I'm sure I'm not the first in this usecase, regardless of the remote API. But I googled a lot without finding a proper way. Maybe I don't search with the right words
Thanks for your help.
I did it like this and it works well:
in apps.py:
from django.apps import AppConfig
from keystoneauth1.identity import v3
from keystoneauth1 import session
from keystoneclient.v3 import client
class MyAppConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'mySharedVar'
auth = v3.Password(auth_url='https://my.keystone.com:5000/v3', user_id='myuserid', password='mypassword',project_id='myprojectid')
ses1 = session.Session(auth=auth)
in my views, I can now access the "share variable" with:
from keystoneauth1.identity import v3
from keystoneauth1 import session
from keystoneclient.v3 import client
#login_required
def list_project(request):
sharedVar=apps.get_app_config('mySharedVar')
keystone = client.Client(session=sharedVar.ses1, include_metadata=True)
pl = keystone.projects.list()
context = {
"title": "Project List",
"projects": pl.data
}
return render(request, "myapp/list_project.html",context=context)
I hope this can help someone.

Django: Stripe & POST request

I am currently trying to implement Stripe Connect in my Django project. Stripe documentations states for Standard accounts:
Assuming no error occurred, the last step is to use the provided code
to make a POST request to our access_token_url endpoint to fetch the
user’s Stripe credentials:
curl https://connect.stripe.com/oauth/token \
-d client_secret=sk_test_Dur3X2cOCwyjlf9Nr7OCf3qO \
-d code="{AUTHORIZATION_CODE}" \
-d grant_type=authorization_code
I now wonder how to send a POST request with Django without form & user action (clicking the submit button)?
Since Standard Connect relies on OAuth for its connection flow:
https://stripe.com/docs/connect/standard-accounts#oauth-flow
so you can use an OAuth python library like Rauth, as you mentioned, to handle the flow.
Also please note that Stripe Python library provides an implementation of the OAuth flow here:
https://github.com/stripe/stripe-python/blob/a938c352c4c11c1e6fee064d5ac6e49c590d9ca4/stripe/oauth.py
You can see an example of its usage here:
https://github.com/stripe/stripe-python/blob/f948b8b95b6df5b57c7444a05d6c83c8c5e6a0ac/examples/oauth.py
The example uses Flask not Django but should give you a good idea in terms of its use.
With regards to the advantages of using an existing OAuth implementation as opposed to implementing the calls directly yourself: one advantage I see is that your code would reuse a library that generally covers all different uses cases (e.g better error handling) and is also well tested.
Thanks to #psmvac I could implement it the 'proper' way now using the oAuth of Stripe. Here some reference/example Django code if anyone is trying the same. Obviously, urls.py has to be configured. This is in my views.py:
def stripe_authorize(request):
import stripe
stripe.api_key = ''
stripe.client_id = 'XYZ'
url = stripe.OAuth.authorize_url(scope='read_only')
return redirect(url)
def stripe_callback(request):
import stripe
from django.http import HttpResponse
# import requests
stripe.api_key = 'XYZ'
## import json # ?
code = request.GET.get('code', '')
try:
resp = stripe.OAuth.token(grant_type='authorization_code', code=code)
except stripe.oauth_error.OAuthError as e:
full_response = 'Error: ' + str(e)
return HttpResponse(full_response)
full_response = '''
<p>Success! Account <code>{stripe_user_id}</code> is connected.</p>
<p>Click here to
disconnect the account.</p>
'''.format(stripe_user_id=resp['stripe_user_id'])
return HttpResponse(full_response)
def stripe_deauthorize(request):
from django.http import HttpResponse
import stripe
stripe_user_id = request.GET.get('stripe_user_id', '')
try:
stripe.OAuth.deauthorize(stripe_user_id=stripe_user_id)
except stripe.oauth_error.OAuthError as e:
return 'Error: ' + str(e)
full_response = '''
<p>Success! Account <code>{stripe_user_id}</code> is disconnected.</p>
<p>Click here to restart the OAuth flow.</p>
'''.format(stripe_user_id=stripe_user_id)
return HttpResponse(full_response)

Flask Blueprint : how to run each apps in different port?

I got a flask application with different apps inside, using BluePrint.
To simplify, I got an API that manages token web authentification (and a lot of other data functions) and a website that should call the API to get the valid token, using a basic auth to start with
The issue is that when the website requests the API, it never gets any feedback from the API.
Requesting the API via POSTMAN works like a charm, but this call below, done from a website route is waiting, waiting, waiting and never ends.
So my assumption is that using the same port for both website and api is the issue
I could of course divides the flask into 2 flask apps with 2 servers, but there are many objects and tools both API and website are sharing, so I dont want to double the job
Thanks.
call from the website
from requests.auth import HTTPBasicAuth
import requests
mod = Blueprint('site', __name__, template_folder='templates/login')
def load_user(username, password):
data = requests.get('http://127.0.0.1:5000/api/login',
auth=HTTPBasicAuth('username', 'password'))
return data
#mod.route('/')
def index():
username = 'jeje'
password = 'jeje'
data = load_user(username, password)
return '<h1>load user<h1>'
the api function
#mod.route('/login')
def login():
resu = True
auth = request.authorization
if not auth or not auth.username or not auth.password:
resu = False
user = USER.query.filter_by(username = auth.username).first()
if not user:
resu = False
if validehash(user.password, auth.password):
period_in_mn = 120
payload = {
'public_id':user.public_id,
'exp' : datetime.datetime.utcnow() + datetime.timedelta(minutes = period_in_mn)
}
token = createtoken(payload, current_app.config['SECRET_KEY'])
if resu:
return jsonify({'token' : token })
else:
return jsonify({'token' : 'unknown'})
I guess your using flask 0.12 instead of 1.0. So whats happening here is that you're requesting a route from within another route.
Your browser requests /, and in your index function, you request /login. But the flask process is still working on the / request from the browser, and can't take any other requests, because flask 0.12 is working on a single core only.
Requesting like this is bad design. You could make a helper function, which returns the same data in different requests (either api or main site), or you could make the browser send another request using AJAX.
Flask 1.0 has multicore support, so I think this might work over there, but I really think you should change your design. This has absolutely nothing to do with blueprints by the way.
from flask import Flask, redirect
import requests
app = Flask(__name__)
#app.route('/')
def index():
result = requests.get('http://127.0.0.1:5000/api/login')
return 'this is the index<br>' + result.text
#app.route('/api/login')
def login():
return 'you are logged in'
Using flask 0.12, times out when visiting http://127.0.0.1:5000/.
Using flask 1.0, returns this is the index
you are logged in when visiting http://127.0.0.1:5000/.

Flask-Dance OAuth with Custom OAuth provider

I m trying to use Flask-Dance OAuth with custom OAuth provider.I have integrated it successfully with mentioned service providers like github.
But when I try to use authorise:github with OAuth using Custom Provider it does not get authorise, account_info.ok prints as false.
What I want to do is using Custom Provider I would be able to authorise any available OAuth Provider.
I can't figure out how to use Custom Provider mentioned at http://flask-dance.readthedocs.io/en/latest/providers.html#custom
Here is my code:
from flask import Flask
from flask_dance.consumer import OAuth2ConsumerBlueprint
from flask import Flask, redirect, url_for
#export OAUTHLIB_INSECURE_TRANSPORT=1
app = Flask(__name__)
app.config['SECRET_KEY']='thisissupposedtobesecretkey'
client_id = "*********************",
client_secret = "********************",
example_blueprint = OAuth2ConsumerBlueprint("github", __name__,
client_id=client_id,
client_secret=client_secret,
scope=None,
base_url="https://api.github.com/",
authorization_url="https://github.com/login/oauth/authorize",
token_url="https://github.com/login/oauth/access_token",
redirect_url=None,
redirect_to=None,
login_url=None,
authorized_url=None,
session_class=None,
backend=None,
)
app.register_blueprint(example_blueprint, url_prefix="/login")
#app.route('/example')
def login():
if not example_blueprint.authorized:
return redirect(url_for('example_blueprint.login'))
try:
account_info=example_blueprint.session.get("/user")
print "i m here ....."
print account_info.ok
return account_info
except Exception as e:
print "i m here .....",e
#
if __name__=='__main__':
app.run(debug=True)
You need to call the authorized method from the OAuth2Session instance which automatically loads tokens for the OAuth provider from the storage. So you need to change the following lines of code:
#app.route('/example')
def login():
if not example_blueprint.authorized:
return redirect(url_for('example_blueprint.login'))
to
#app.route('/example')
def login():
if not example_blueprint.session.authorized:
return redirect(url_for('example_blueprint.login'))
Cheers

Django test Client simulate logged in user

I am new to Django test Client. I want to test a view that uses #login_required . I was wondering if I can do that using the simple Client() like this:
>>> from django.test import Client
>>> c = Client()
>>> r = c.get("/libros/nuevo_libro/perfil/farseer/")
But, of course, the response is always a 302:
>>> r.status_code
302
Cause the #login_required decorator redirects it to the login page.
Is it possible to simulate in this kind of tests that the user is logged in?
The test client has a login method.
c = Client()
c.login(username='fred', password='secret')
response = c.get("/libros/nuevo_libro/perfil/farseer/")