Need help understanding how the token request process works. I want to submit a post to Stocktwits via python. I understand that I need a token and need to submit criteria to 'https://api.stocktwits.com/api/2/oauth/token'
How do I receive and store the token?
import requests, json, urllib
from oauth2 import *
client_id = 'secret'
client_secret = 'secret'
req_token_URL = 'https://api.stocktwits.com/api/2/oauth/token'
Related
I'd like to have many different clients be able to access my django website (more specifically its API) but I'm not sure how to do this with django-allauth, dj-rest-auth and simplejwt.
My current client app is using the built in django template engine and is set up with django-allauth for social authentication (Google etc). It's working using the documented installation recommendations.
I would now like to create different types of clients that aren't using the django template engine (e.g. Angular, Vue, flutter mobile etc) but I'm confused how dj-rest-auth is used so that it scales to support any number of client types.
Using Google social sign in as an example, when I create a new client, I have to register a new redirect_uri specific to that client.
To test this all out, I created a simple flask app with a single link so that I can retrieve a "code/access_token" before sending it to my Django app. The link is created using the following...
var codeRequestUrl =
`https://accounts.google.com/o/oauth2/v2/auth?\
scope=email&\
access_type=offline&\
include_granted_scopes=true&\
response_type=code&\
state=state_parameter_passthrough_value&\
redirect_uri=http%3A//127.0.0.1:5000/callback&\
client_id=${clientId}`;
...and the code is retrieved at the '/callback' endpoint in flask...
#app.route("/callback", methods=['GET'])
def redirect():
code = request.args.get('code', '')
req = requests.post('http://127.0.0.1:8000/api/dj-rest-auth/google/', data={'code':code})
return "done..."
...from where I send an x-www-form-urlencoded POST request back to a dj-rest-auth endpoint that is set up as per its documentation...
class GoogleLogin(SocialLoginView):
callback_url = 'http://127.0.0.1:5000/callback'
adapter_class = GoogleOAuth2Adapter
client_class = OAuth2Client
...
urlpatterns += [
...
path('dj-rest-auth/google/', GoogleLogin.as_view(), name='google_login'),
....
]
Django then successfully returns an access_token, refresh_token and some info about the logged in user.
But this isn't something that scales well. If I were to also create an Angular client, I'd need to register a different callback (because the Angular client would be running on a different port and/or address, and I'd also need another path set up in urls.py and associate it with a new SocialLoginView subclass that can handle the different callback_url (redirect_uri).
And with all this in mind, I have no idea how to do all of this with a flutter mobile app, which as far as I'm aware, has no concept of a callback_url, so I'm not sure how making a POST request to .../dj-rest-auth/google/ would even work given that I'd instantly get a redirect_uri_mismatch error.
Have I got it backwards and the client registered at Google is the Angular, Vue, Flash etc app? That would mean that each client would have to handle its own client_id and client_secret, which then seems to bypass django-allauth's and dj-rest-auth's functionality.
I feel like I'm misinterpreting this, so I would really appreciate some suggestions.
I feel confident enough to answer my own question.
In short, yes, multiple clients (including thirdparty) is a reasonably straight forward process. Unfortunately a lot of the blog posts and tutorials that exist take the perspective of a 'second party' client, which really confuses things. The result is a lot of error messages relating to the redirect_uri.
To their credit, the Google docs for their example Flask app was exactly what I needed, but there are a couple of observations that are really important, and what caused so much confusion for me.
First, and most important, the callback (redirect_uri) is not needed in Django at all. In Django, something like this is all that is required.
from allauth.socialaccount.providers.oauth2.client import OAuth2Client
from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter
class GoogleLogin(SocialLoginView):
adapter_class = GoogleOAuth2Adapter
client_class = OAuth2Client
urlpatterns += [
...
path('auth/google/', GoogleLogin.as_view(), name='google_login'),
...
]
So no callback attribute is required. The reason for this is that the Flask (or thirdparty app) handles all of the Google side authentication.
The second observation was that the redirect_uri in the Flask app seemed have have to be the same for both the "code" request step, and the "access_token" step.
You can see it in the linked example where the oauth2callback function (which handles the redirect_uri), but I've modified for use with dj-rest-auth
#app.route('/')
def index():
if 'credentials' not in flask.session:
return flask.redirect(flask.url_for('oauth2callback'))
credentials = json.loads(flask.session['credentials'])
if credentials['expires_in'] <= 0:
return flask.redirect(flask.url_for('oauth2callback'))
else:
data = {'access_token': credentials['access_token']}
headers = headers = {'Content-Type': 'application/x-www-form-urlencoded'}
r = requests.post(f'{URL_ROOT}/api/auth/google/', data=data, headers=headers)
response_json = json.loads(r.text)
access_token = response_json['access_token'] # JWT Access Token
refresh_token = response_json['refresh_token']
# Make a query to your Django website
headers = headers = {'Authorization': f'Bearer {access_token}'}
r = requests.post(f'{URL_ROOT}/api/object/{OBJECT_ID}/action/', data=data, headers=headers)
# do stuff with r
#app.route('/oauth2callback')
def oauth2callback():
if 'code' not in flask.request.args:
auth_uri = ('https://accounts.google.com/o/oauth2/v2/auth?response_type=code'
'&client_id={}&redirect_uri={}&scope={}').format(CLIENT_ID, REDIRECT_URI, SCOPE)
return flask.redirect(auth_uri)
else:
auth_code = flask.request.args.get('code')
data = {'code': auth_code,
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'redirect_uri': REDIRECT_URI,
'grant_type': 'authorization_code'}
r = requests.post('https://oauth2.googleapis.com/token', data=data)
flask.session['credentials'] = r.text # This has the access_token
return flask.redirect(flask.url_for('index'))
So in summary, it's a bit like this:
On the index/home page, the user presses a "Login" html anchor that points to /login.
Flask doesnt have any credentials, so redirects to /oauth2callback to begin authentication.
First, the "code" is retrieved using Googles' GET /auth endpoint, and by using your app's client id.
The redirect_uri ensures the code flow goes back to itself, but this time, with the "code" now know, do a POST request to Google's /token endpoint using your apps's client id and client secret. Again, the redirect_uri is the same (/oauth2callback).
Now that Googles's access_token is known, the Flask app redirects back to /index (although it could be anywhere at this point)
Back in /index, the Flask app now has Google's "access_token". Use that to log into Django's dj-rest-auth endpoint that you created.
Django will then return its own access_token and refresh_token, so continue to use those as needed.
I hope this helps.
Note that your flask app will need to be registered as a new Web App with Google's OAuth2 console (so it has it's own client id and client secret). In other words, don't reuse what you may have already created with an existing Django allauth implementation (which was my scenario). Each thirdparty app maker will handle their own OAuth2 credentials.
I'm trying to grab the number of likes on my Instagram posts with this api.
I can successfully call the endpoints using the access token generated from the Graph API Explorer, but I'm trying to find a way to generate the access token programmatically. The API docs have examples on how to request an access token from a Web/Mobile UI, but I'm wanting this script to sit on a server and run as a cronjob, etc.
I've seen some resources on how to convert the short-lived token into a long-lived token from code, but is it possible to generate the initial token from code?
This approach might not work for everyone, but I ended up using a webdriver to automate the OAuth login process.
import requests
from selenium import webdriver
app_id = 1234567890
secret = '...'
redirect = 'https://whatever' # needs to be added in app settings
fb_username = '...'
fb_password = '...'
login_url = 'https://www.facebook.com/v14.0/dialog/oauth'
login_url += f'?client_id={app_id}&redirect_uri={redirect}'
b = webdriver.Firefox()
b.get(login_url)
b.find_element_by_xpath('//*[#id="email"]').send_keys(fb_username)
b.find_element_by_xpath('//*[#id="pass"]').send_key(fb_password)
b.find_element_by_xpath('//*[#id="loginbutton"]').click()
code = b.current_url.split('=')[1]
url = 'https://graph.facebook.com/v14.0/oauth/access_token?'
url += f'client_id={app_id}'
url += f'&redirect_uri={redirect}'
url += f'&client_secret={secret}'
url += f'&code={code}'
resp = requests.get(url)
if resp.status_code == 200:
print(resp.json()['access_token'])
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.
This question already has answers here:
How do you authenticate a websocket with token authentication on django channels?
(8 answers)
Closed 3 years ago.
I am using a frontend framework (Vuejs) and django-rest-framework for the REST API in my project. Also, for JSON web token authentication I am using django-rest-framework-jwt. After a successful login, the user is provided with a token. This token is passed into every request to fetch any API related stuff.
Now I would like to integrate django channels into my project. So, after successful login, when the token is received in the client side, I would like to initiate a websocket connection. Then on the server (consumer), I would like to check if the requested user is not anonymous. If the requested user is anonymous, I would like to close the connenction or else accept it.
This is how I have till now:
client side:
const socket = new WebSocket("ws://" + "dev.site.com"+ "/chat/");
routing.py:
channel_routing = [
route("websocket.connect", ws_connect),
...
...
]
consumers:
def ws_connect(message):
# if the user is no anonymous
message.reply_channel.send({
"accept": True
})
# else
message.reply_channel.send({
"close": True
})
In the documentation there's a decorator #channel_session_user_from_http which will provide a message.user. But I am using a token instead of a session. How can I check a user on connection when using token authentication, so that I can accept or close connection. Or, if there is a better way could you please advise me with it. Thank you.
The problem is that the browsers do not support passing jwt auth headers on websocket upgrade, so that's basically it. I faced this problem some time ago and came up with the solution of passing the token via query parameters - note that this is totally insecure without TLS as you expose the authentication in the URI. I don't have the access to the exact code anymore, but here is the idea:
from channels.generic.websockets import JsonWebsocketConsumer
from channels.handler import AsgiRequest
from rest_framework_jwt.serializers import VerifyJSONWebTokenSerializer
from jwt.exceptions import InvalidTokenError
from rest_framework.exceptions import ValidationError
class Consumer(JsonWebsocketConsumer):
def connect(self, message, **kwargs):
# construct a fake http-like request object from the message
message.content.setdefault('method', 'FAKE')
request = AsgiRequest(message)
# validate the token
try:
VerifyJSONWebTokenSerializer().validate(request.GET)
super().connect(message, **kwargs)
except (KeyError, InvalidTokenError, ValidationError,):
# token is either not available or invalid
# so we disconnect the user
message.reply_channel.send({'close': True})
Register the consumer with
channel_routing = [
...
route_class(Consumer, path=r'^my-ws-endpoint$'),
]
On browser side, you can establish the websocket connection by passing the token as query parameter in the websocket URI:
let token: string = 'my-token'; // get the token
let wsHandler: $WebSocket = new $WebSocket('wss://example.com/my-ws-endpoint/?token=' + token, ...);
You can then extract the auth check code in a decorator similar to #channel_session_user_from_http and just decorate your connection routines, or extract the code to a mixin if you use class-based routes.
I would like to repeat though that this approach is totally insecure without using encryption, so in production you URIs should start with https/wss.
Edit: here is a pretty nice solution for DRF token auth, suitable for both function-based and class-based routes. It has pretty much the same approach as mine, constructing a request object and passing it to the authenticator.
#hoefling's answer was my guide. I was confused about two things on authenticating a user.
What to do with the token?
You can pass the token as a query string and get that query params. Read more about how to get the query params here.
Or if you are already passing it in the request's authorization header, you can get it from there like #hoefling did with his answer. Remeber to first fake that request.
How validate that token and get the user?
Finally VerifyJSONWebTokenSerializer class was all I needed to validate the token, and get that token's user object. (Thanks #hoefling!) You can read the actual code of django-rest-framework-jwt here.
So, I ended up doing this way:
def ws_connect(message):
message.content.setdefault('method', 'FAKE')
django_request = AsgiRequest(message)
token = django_request.GET['token'].split(' ')[1]
try:
data = {'token': token}
valid_data = VerifyJSONWebTokenSerializer().validate(data)
user = valid_data['user']
...
...
message.reply_channel.send({
"accept": True
})
except (KeyError, InvalidTokenError, ValidationError,):
...
...
message.reply_channel.send({
"text": "Authentication error",
"close": True
})
I'm trying to get my facebook app access token en my django project. This is my function code:
import json
import urllib2
import requests
def fb_update_app_token():
app_id = MY_APP_ID
secret = MY_APP_SECRET
grant = '{0}/{1}'.format(app_id, secret)
url = u'https://graph.facebook.com/oauth/access_token?' \
u'client_id={0}&client_secret={1}&grant_type={2}'.format(
app_id,
secret,
grant,
)
print url
response = urllib2.Request(url)
print "RESPONSE"
print response
token = json.loads(urllib2.urlopen(response).read())
print "TOKEN"
print token
return token
But my console output is:
https://graph.facebook.com/oauth/access_token?client_id=app_id&client_secret=sectret&grant_type=app_id/secret
RESPONSE
<urllib2.Request instance at 0x104bcdb48>
[30/Jul/2015 17:05:31] "POST /dashboard/ajax-update-access-token/ HTTP/1.1" 500 14003
And the grap api response is HTTP Error 400: Bad Request.
Can somebody tell me what is wrong with it? Thankyou so much.
If you want to connect your application with Facebook or any other social media you just don't care about tokens. Middleware does:
http://django-social-auth.readthedocs.org/en/latest/configuration.html
&
http://django-social-auth.readthedocs.org/en/latest/backends/facebook.html