Discord's OAuth2 integration allows developers to authenticate users through their Discord accounts, providing a seamless login experience while gaining access to user profile data. In this guide, we'll walk through implementing Discord social login in a Python web application using Flask.
Setting Up Your Discord Application
- Go to the Discord Developer Portal.
- Click "New Application" and give it a name.
- Navigate to the "OAuth2" section.
- Note your Client ID and generate a Client Secret.
- Add a redirect URI (e.g., http://127.0.0.1:5000/callback).
Install the required dependencies by creating a requirements.txt file.
discord.py==2.5.2
Flask==3.1.0
python-dotenv==1.0.1
requests==2.32.3
requests-oauthlib==2.0.0
flasgger==0.9.7.1
Configuration
Create a config.py file to manage your configuration.
import os
from dotenv import load_dotenv
load_dotenv()
class Config:
CLIENT_ID = os.getenv('CLIENT_ID')
CLIENT_SECRET = os.getenv('CLIENT_SECRET')
REDIRECT_URI = os.getenv('DISCORD_REDIRECT_URI')
AUTHORIZATION_BASE_URL = os.getenv('AUTHORIZATION_BASE_URL')
TOKEN_URL = os.getenv('TOKEN_URL')
SCOPE = os.getenv('SCOPE').split(',')
Create a .env file with your Discord credentials.
CLIENT_ID=your_client_id_here
CLIENT_SECRET=your_client_secret_here
DISCORD_REDIRECT_URI=http://127.0.0.1:5000/callback
AUTHORIZATION_BASE_URL=https://discord.com/api/oauth2/authorize
TOKEN_URL=https://discord.com/api/v10/oauth2/token
SCOPE=identify,email
Implementing the Flask Application
Here's the complete implementation (app.py) with explanations for each part.
from flask import Flask, redirect, request, session
from requests_oauthlib import OAuth2Session
import os
from dotenv import load_dotenv
import requests
from flasgger import Swagger
from config import Config
load_dotenv()
app = Flask(__name__)
app.secret_key = os.urandom(24)
# Swagger configuration for API documentation
app.config['SWAGGER'] = {
'title': 'Discord Login API',
'uiversion': 3
}
swagger = Swagger(app)
1. Login Route
This route initiates the OAuth2 flow by redirecting users to Discord's authorization page.
@app.get('/login')
def login():
"""
Redirects user to Discord for authorization.
---
responses:
302:
description: Redirect to Discord's authorization page.
"""
discord = OAuth2Session(
Config.CLIENT_ID,
redirect_uri=Config.REDIRECT_URI,
scope=Config.SCOPE
)
authorization_url, state = discord.authorization_url(Config.AUTHORIZATION_BASE_URL)
session['oauth_state'] = state
return redirect(authorization_url)
2. Callback Route
After Discord authorization, users are redirected here. This route exchanges the authorization code for an access token.
@app.get("/callback/")
def callback():
"""
Handles the callback from Discord, exchanges the code for an access token.
---
parameters:
- name: code
in: query
type: string
required: true
description: The authorization code received from Discord.
- name: state
in: query
type: string
required: true
description: The state returned by Discord.
responses:
200:
description: Returns the access token and other data.
"""
code = request.args.get('code')
state = request.args.get('state')
data = {
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': Config.REDIRECT_URI
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
r = requests.post(
Config.TOKEN_URL,
data=data,
headers=headers,
auth=(Config.CLIENT_ID, Config.CLIENT_SECRET)
)
r.raise_for_status()
return r.json()
3. User Info Route
This endpoint fetches user information using the obtained access token.
@app.get('/getuserinfo/')
def get_userinfo():
"""
Retrieves user information from Discord using the access token.
---
parameters:
- name: access_token
in: query
type: string
required: true
description: The access token obtained from the callback.
responses:
200:
description: Returns the user information.
"""
access_token = request.args.get('access_token')
headers = {
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/x-www-form-urlencoded'
}
r = requests.get('https://discord.com/api/v10/users/@me', headers=headers)
r.raise_for_status()
user = r.json()
user['avatar'] = get_user_avatar(user)
return user
4. Avatar Helper Function
This function constructs the proper URL for the user's avatar.
def get_user_avatar(user):
"""Get user profile image"""
if user['avatar'] is None:
return "https://cdn.discordapp.com/embed/avatars/0.png"
return f"https://cdn.discordapp.com/avatars/{user['id']}/{user['avatar']}.png?size=1024"
Running the Application
Start the Flask development server.
python3 app.py
Testing the Implementation
- Visit http://localhost:5000/login in your browser.
- You'll be redirected to Discord's authorization page.
- After authorization, you'll be redirected back to your callback URL with an access token.
- Use Swagger UI at http://localhost:5000/apidocs to test the /getuserinfo/ endpoint.
GitHub Repository
You can find a complete implementation of this Discord login sample at: GitHub Repository Link