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
Related
I'm trying to create a Flask app that can browse to the user's Google Drive to select a file to convert to CSV format.
I found online a code to add Google Login (see below), but I don't understand how to print all files from the logged user. I figured out I need to add https://www.googleapis.com/auth/drive.file to the scope but I can't understand how to list all files from an authorized user (current_user.paying==True) in index. I know that this could be done with the following code, but I'm not sure how to defined the credentials.
In callback an access token is already generated, so I tried to access the Google Drive API to just search for the user's Google sheets:
from httplib2 import Http
from apiclient import discovery
from oauth2client import file
with open("./credentials.json", 'w') as outfile:
json.dump(token_response.json(), outfile)
store = file.Storage("./credentials.json")
credentials = store.get()
drive = discovery.build("drive", "v3", http=credentials.authorize(Http()))
files = drive.files().list(q="mimeType='application/vnd.google-apps.spreadsheet'").execute()
But this gives me an "KeyError: '_module'" error that's not very informative.
This is the entire Flask code with Google Login
import os
import requests
import json
from httplib2 import Http
from apiclient import discovery
from oauthlib.oauth2 import WebApplicationClient
from flask import Flask, redirect, request, url_for
from flask_login import LoginManager, current_user, login_required, login_user, logout_user
from flask_login import UserMixin
authorized_users = ["myuser#gmail.com"]
class User(UserMixin):
def __init__(self, id_, paying):
self.id = id_
self.paying = paying
#staticmethod
def get(user_email):
if user_email not in authorized_users:
user = User(user_email, False)
else:
user = User(user_email, True)
return user
# Configuration
GOOGLE_CLIENT_ID = os.environ.get("GOOGLE_CLIENT_ID", None)
GOOGLE_CLIENT_SECRET = os.environ.get("GOOGLE_CLIENT_SECRET", None)
GOOGLE_DISCOVERY_URL = ("https://accounts.google.com/.well-known/openid-configuration")
# Flask app setup
app = Flask(__name__)
app.secret_key = os.environ.get("SECRET_KEY") or os.urandom(24)
# User session management setup
login_manager = LoginManager()
login_manager.init_app(app)
# OAuth 2 client setup
client = WebApplicationClient(GOOGLE_CLIENT_ID)
# Flask-Login helper to retrieve a user from our db
#login_manager.user_loader
def load_user(user_email):
return User.get(user_email)
def get_google_provider_cfg():
return requests.get(GOOGLE_DISCOVERY_URL).json()
google_provider_cfg = get_google_provider_cfg()
token_endpoint = google_provider_cfg["token_endpoint"]
print("Google provider cfg", google_provider_cfg)
ACCESS_TOKEN_URI = 'https://www.googleapis.com/oauth2/v4/token'
#app.route("/")
def index():
if current_user.is_authenticated:
if current_user.paying:
return f"<p>Hello, {current_user.id}! You're logged in!</p><br/><a class='button' href='/logout'>Logout</a>"
else:
return "Not authorized!<br/><a class='button' href='/logout'>Logout</a>"
else:
return f"<a class='button' href='/login'>Google Login</a>"
#app.route("/login")
def login():
# Find out what URL to hit for Google login
google_provider_cfg = get_google_provider_cfg()
authorization_endpoint = google_provider_cfg["authorization_endpoint"]
# Use library to construct the request for Google login and provide
# scopes that let you retrieve user's profile from Google
request_uri = client.prepare_request_uri(
authorization_endpoint,
redirect_uri=request.base_url + "/callback",
scope=["openid", "email", "profile", "https://www.googleapis.com/auth/drive.file"],
)
return redirect(request_uri)
#app.route("/login/callback")
def callback():
# Get authorization code Google sent back to you
code = request.args.get("code")
# Prepare and send a request to get tokens! Yay tokens!
token_url, headers, body = client.prepare_token_request(
token_endpoint,
authorization_response=request.url,
redirect_url=request.base_url,
code=code
)
token_response = requests.post(
token_url,
headers=headers,
data=body,
auth=(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET),
)
# Parse the tokens!
client.parse_request_body_response(json.dumps(token_response.json()))
# Now that you have tokens (yay) let's find and hit the URL
# from Google that gives you the user's profile information,
# including their Google profile image and email
userinfo_endpoint = google_provider_cfg["userinfo_endpoint"]
uri, headers, body = client.add_token(userinfo_endpoint)
userinfo_response = requests.get(uri, headers=headers, data=body)
if userinfo_response.json().get("email_verified"):
# unique_id = userinfo_response.json()["sub"]
users_email = userinfo_response.json()["email"]
picture = userinfo_response.json()["picture"]
users_name = userinfo_response.json()["given_name"]
else:
return "User email not available or not verified by Google.", 400
# Create a user in your db with the information provided
# by Google
user = User(users_email, False)
#
# # Doesn't exist? Add it to the database.
# if not User.get(unique_id):
# User.create(unique_id, users_name, users_email, picture)
print("Logging", users_name, users_email, picture)
# Begin user session by logging the user in
login_user(user)
# Send user back to homepage
return redirect(url_for("index"))
#app.route("/logout")
#login_required
def logout():
logout_user()
print("Logging out")
return redirect(url_for("index"))
if __name__ == "__main__":
app.run(ssl_context="adhoc", debug=True)
An easy way to obtain the credentials is
Visit the Google Drive API Quickstart
Click on Enable Drive
Either note down the Client Id and Client Secret or click on DOWNLOAD CLIENT CONFIGURATION and open the generated and downloaded credentials.json file
Alternatively, you can
visit the Google Cloud Console
Choose APIs & Services -> Credentials
Click on +CREATE CREDENTIALS
UPDATE
KeyError: '_module' is an error originating from retrieving a json file with an incorrect structure.
Possible reasons:
Using the API Client Library that is expecting a client_secrets.json file - this is different from the credentials.json file you obtain from the Google Drive API Quickstart
Storing not valid credentials in the json file
Storing the json file in another folder than the py file
Using invalid scopes
I understand that you use the WebApplicationClient and need to create respective client credentials for a Web Server.
The json file should have the content of type
{"web":{"client_id":"XXXX.apps.googleusercontent.com","project_id":"XXXX","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"XXX","redirect_uris":["https://script.google.com/oauthcallback"]}}
ALSO:
The scope https://www.googleapis.com/auth/drive.file is not sufficient to list all files on a user's drive - it only gives you access to the files that were created / opened with your app. More information here.
I recommend you for debugging purposes to give your App in the first generous scopes. Once you solve the credentials issue and your App works - see how far you can restrict the scopes without impacting the functionality of your App.
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'
I have written a simple Python Flask API which does operations like adding data to Database and getting data from Database, there is no UI for this API, Now I want to implement OAuth authentication system for this simple API, As there is NO GUI, I cant use google or FB Oauth Providers which redirects users to there login page.
In simple words, i want to create my own GUI less oauth Authentication system which secures my API as any user who wants to access my API should pass through this authentication system by passing access token in a header
I need Oauth Authentication system of my own for the API's below:
from flask import Flask, redirect, url_for, session
from flask import Flask,jsonify,request,make_response
from flask_login import login_user,logout_user,current_user,login_required,LoginManager,login_manager
from flask_oauth import OAuth
import json
from flask_mysqldb import MySQL
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_oauthlib.provider import OAuth1Provider
app = Flask(__name__)
class MYWIFI(db.Model):
__tablename__ = 'MYWIFI'
id = db.Column('id', db.Integer, primary_key=True)
data = db.Column('data', db.Unicode)
def __init__(self, id, data):
self.id = id
self.data = data
#app.route('/getall')
def getall():
access_token = get_access_token()
if access_token is None:
return redirect(url_for('login'))
else:
languages = [u.__dict__ for u in db.session.query(MYWIFI).all()]
for d in languages:
del d['_sa_instance_state']
print(languages)
languagesJSON = json.dumps(languages)
return languagesJSON
#app.route('/insert', methods=['GET','POST'])
def insert():
access_token = get_access_token()
if access_token is None:
return redirect(url_for('login'))
else:
if request.method == 'POST':
insert = request.get_json()
id = insert['id']
data = insert['data']
print id
print data
new = MYWIFI(id, data)
db.session.add(new)
db.session.commit()
return "Success"
def main():
app.run()
if __name__ == '__main__':
main()
Please can anyone help me in kick starting this
I appreciate for this help
If I understood correctly, what you want is to build API endpoints which are protected by OAuth 2.0 tokens. If that's the case you as the API builder does not have to worry how token obtaining process happens. The client that consumes your APIs must perform the token obtaining and pass them to your end.
About sending in headers, try to stick with standards already exist in the OAuth 2 domain. RFC6750 defines how to use tokens once a client obtains them. It defines bearer authentication schema to transmit access tokens. Check section 2.1 to how header is set.
GET /resource HTTP/1.1
Host: server.example.com
Authorization: Bearer mF_9.B5f-4.1JqM
Where mF_9.B5f-4.1JqM is the access token. Once your API receives a request, from your end you must validate the access token before granting access. For this there is RFC7662 which define the methodology to validate access token against the authorization server. See section 2 Introspection endpoint to get an understanding of it. Alternatively, access token can come in a JWT format thus allowing it to be self contained.
Unfortunately, I do not have code for proposed solutions. They will considerable amount of codes. But I suggest you to separate authorization logic from your code. That means validation of authorization must be a separate module in your python code. But below I give a suggestion with my python knowledge.
#app.route('/insert', methods=['GET','POST'])
def insert():
access_token = get_access_token()
# Auth Validation for insert - This is based on access tokens
# If token is invalid/not-present, exception is thrown with HTTP 401 - unauthorized
my_auth_module.validate_access_token(access_token)
if request.method == 'POST':
insert = request.get_json()
id = insert['id']
Also one final thing, your API should not worry about redirection for login. Let it be handled by your API client upon the 401 - Unathorized response.
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)
I want to allow the django users to use a key in the api urls for authentication.
I do not have OAUTH set up yet so I guess the key could be a sesion key or a digest key.
I'm having 2 problems.
I've tried sending this request:
http://192.166.166.11:8000/api?task=search&term=115&csrf_token=s69WAIZqlCTur1XZQr72QhCc7fzqaRtM
First of all, I've tried using the csrf_token but it does not work.
It takes me to the login page.
Secondly, I do not know how to retrieve csrf_token of other users (the admin is trying to get their csrf_tokens).
My attempt:
x = User.objects.get(username='someone')
x.get_session_auth_hash()
gives me the user's authentication hash but it is a different value.
Can someone please guide me get these two problems sorted out?
You are creating a token-based authentication. You already mentioned OAUTH as one option, and I strongly recommend using one of the existing implementations like django-oauth-toolkit. However, you can also create your own quick solution to create a token-based authentication.
Disclaimer: This is for demo purposes only. Do not copy it in any existing project. It will make your application vulnerable.
First, we create an additional model handling the authentication tokens:
/auth_tokens/models.py
from django.db import models
from django.conf import settings
import string, random
def random_string(length = 64, chars=string.ascii_uppercase + string.ascii_lowercase + string.digits):
return ''.join(random.choice(chars) for x in range(length))
class AuthToken(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
token = models.CharField(max_length=64, default=random_string)
/auth_tokens/middleware.py
from auth_tokens.models import AuthToken
class AuthTokenMiddleware:
def process_request(self, request):
token = request.GET.get('auth', None)
if not token:
return
token = AuthToken.objects.get(token=token)
request.user = token.user
return request
Including the middleware into your settings.MIDDLEWARE_CLASSES should enable you to add ?token=<token> to your URL to login your users.
I ended up using token authentication:
http://www.django-rest-framework.org/api-guide/authentication/
so I'd like to share the workflow.
First, you need to do the set up. In settings.py, modify INSTALLED_APPS and add REST_FRAMEWORK as in documentation.
Then you need to run python manage.py syncdb because it needs to add some tables.
Then, you need to add some urls to urls.py to route the api.
You can create and retrieve tokens using this code:
from rest_framework.authtoken.models import Token
token = Token.objects.create(user=User.objects.get(username='john'))
print token.key
Lastly, you'll have to modify your view which depends on whether you're using a function based or class based view.
Here is a function based view I used:
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.decorators import authentication_classes, permission_classes
from rest_framework.decorators import api_view
#api_view(['GET', 'POST'])
#authentication_classes((TokenAuthentication,))
#permission_classes((IsAuthenticated,))
#login_required
def mybooks(request):
entries = Book.objects.all()
return render(request, 'mybooks.html', {'entries': entries})
Lastly, to test it out:
import requests
token = '243124c52f7583e320d043c4395bd99f63344035'
headers = {'Authorization' : 'Token {}'.format(token)}
page = requests.post('http://127.0.0.1:8000/mybooks/', headers=headers)
print page.content
Note that in my case I do not need define plain serialization since I have an advanced custom serialization and that is not the topic here.
Django doesn't provide API Keys out of the box.
Use API providers such as Tastypie to have this feature