-
Notifications
You must be signed in to change notification settings - Fork 2.9k
OAuth2 Python Example
This guide shows example code for a web service that connects to a reddit account. For details on each step, see the full OAuth2 login docs. For a simpler use case, see the script app quick start guide.
Go to your app preferences. Click the "Create app" or "Create another app" button. Fill out the form like so:
- name: My Example App
- App type: Choose the web app option
- description: You can leave this blank
- about url: You can leave this blank
- redirect url: http://localhost:65010/reddit_callback
Note: For an actual app, you'd want to set this to you a URL that your user could access rather than localhost
. It is also STRONGLY recommended to serve your app over https
to protect the authorization_code
from middlemen.
Hit the "create app" button. Make note of the client ID and client secret. For the rest of this page, it will be assumed that:
- Your app's client ID is:
p-jcoLKBynTLew
- Your app's client secret is:
gko_LXELoV07ZBNUXrvWZfzE3aI
Note: You should NEVER post your client secret in public. The client IDs, secrets, tokens and passwords used here are, obviously, fake and invalid.
The following Python example relies on the Flask web framework and the Python requests library
For the sake of the example, configuration values are hardcoded into the python script and imports are done in the functions used. reddit recommends using external configuration, such as an ini file and following PEP 008 guidelines for organizing imports at the top of the file.
Serve a webpage on http://localhost:65010/
that creates an "authorization" link out to reddit.com:
CLIENT_ID = "p-jcoLKBynTLew"
CLIENT_SECRET = "gko_LXELoV07ZBNUXrvWZfzE3aI"
REDIRECT_URI = "http://localhost:65010/reddit_callback"
from flask import Flask
app = Flask(__name__)
@app.route('/')
def homepage():
text = '<a href="%s">Authenticate with reddit</a>'
return text % make_authorization_url()
def make_authorization_url():
# Generate a random string for the state parameter
# Save it for use later to prevent xsrf attacks
from uuid import uuid4
state = str(uuid4())
save_created_state(state)
params = {"client_id": CLIENT_ID,
"response_type": "code",
"state": state,
"redirect_uri": REDIRECT_URI,
"duration": "temporary",
"scope": "identity"}
import urllib
url = "https://ssl.reddit.com/api/v1/authorize?" + urllib.urlencode(params)
return url
# Left as an exercise to the reader.
# You may want to store valid states in a database or memcache,
# or perhaps cryptographically sign them and verify upon retrieval.
def save_created_state(state):
pass
def is_valid_state(state):
return True
if __name__ == '__main__':
app.run(debug=True, port=65010)
Running the above code serves a simple text page with a URL that sends the user to reddit to request that this app be able to access their reddit identity. If the user chooses to "allow" the app to access their reddit identity, they'll be sent to the REDIRECT_URI (configured during app registration) of http://localhost:65010/reddit_callback
with additional information in the query parameters. The Flask server needs to be configured to respond to that URL:
from flask import abort, request
@app.route('/reddit_callback')
def reddit_callback():
error = request.args.get('error', '')
if error:
return "Error: " + error
state = request.args.get('state', '')
if not is_valid_state(state):
# Uh-oh, this request wasn't started by us!
abort(403)
code = request.args.get('code')
# We'll change this next line in just a moment
return "got a code! %s" % code
The Flask app now handles reddit's redirect back into the app, but doesn't quite do anything yet. The next step is to take that code and get an OAuth access token with it. The code is only good once!
First, change the last line of that previous function from:
return "got a code! %s" % code
to:
return "got an access token! %s" % get_token(code)
Then, add the get_token(code)
function:
import requests
import requests.auth
def get_token(code):
client_auth = requests.auth.HTTPBasicAuth(CLIENT_ID, CLIENT_SECRET)
post_data = {"grant_type": "authorization_code",
"code": code,
"redirect_uri": REDIRECT_URI}
response = requests.post("https://ssl.reddit.com/api/v1/access_token",
auth=client_auth,
data=post_data)
token_json = response.json()
return token_json["access_token"]
Alright, take a deep breath. The hard part is over. Now you have a token, and your app can use it to make requests to the reddit API on behalf of that user until it expires. (If it expires, the user will need to re-authorize your app. Alternatively, if you need to have ongoing access on behalf of the user, you could use a refresh token. That flow is beyond the scope of this example.)
As an example, we'll request the user's name and display it to them as the content of the page we show the user after they authorize the app. First, change the last part of the callback function one more time from:
return "got an access token! %s" % get_token(code)
to:
access_token = get_token(code)
return "Your reddit username is: %s" % get_username(access_token)
Then add the get_username
function:
def get_username(access_token):
headers = {"Authorization": "bearer " + access_token}
response = requests.get("https://oauth.reddit.com/api/v1/me", headers=headers)
me_json = response.json()
return me_json['name']
That's it! Now you can run your app. See the full source file