CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-praw

Python Reddit API Wrapper - simple access to Reddit's API

Pending
Overview
Eval results
Files

authentication.mddocs/

Authentication

PRAW provides comprehensive OAuth 2.0 authentication support for both script applications and web applications. It handles token management, refresh tokens, and supports multiple authentication flows while maintaining security and following Reddit's API guidelines.

Capabilities

Auth Class - Authentication Management

Handle OAuth 2.0 authentication flows and token management.

class Auth:
    def __init__(self, reddit): ...
    
    def authorize(self, code: str):
        """
        Complete OAuth 2.0 authorization with authorization code.
        
        Parameters:
        - code: Authorization code from Reddit OAuth callback
        
        This method exchanges the authorization code for access and refresh tokens.
        Used in web application authentication flow.
        """
    
    def implicit(
        self,
        access_token: str,
        expires_in: int,
        scope: str
    ):
        """
        Set implicit OAuth 2.0 authorization.
        
        Parameters:
        - access_token: Access token from implicit flow
        - expires_in: Token expiration time in seconds
        - scope: Space-separated list of granted scopes
        
        Used for implicit grant flow (less secure, not recommended).
        """
    
    def url(
        self,
        scopes: list,
        state: str,
        *,
        duration: str = "temporary",
        implicit: bool = False,
        **kwargs
    ) -> str:
        """
        Generate OAuth 2.0 authorization URL.
        
        Parameters:
        - scopes: List of requested scopes
        - state: Random string to prevent CSRF attacks
        - duration: "temporary" or "permanent" token duration
        - implicit: Use implicit grant flow
        
        Returns:
        Authorization URL for user to visit
        """
    
    def revoke_token(
        self,
        token: str = None,
        *,
        revoke_refresh: bool = True,
        **kwargs
    ):
        """
        Revoke access or refresh token.
        
        Parameters:
        - token: Token to revoke (defaults to current access token)
        - revoke_refresh: Also revoke refresh token
        
        Invalidates the specified token on Reddit's servers.
        """
    
    def scopes(self) -> set:
        """
        Get currently authorized scopes.
        
        Returns:
        Set of scope strings currently granted
        """

Authentication Flows

Script Application Authentication

For personal scripts and automation tools running on your own machine.

# Script authentication configuration
reddit = praw.Reddit(
    client_id="your_client_id",
    client_secret="your_client_secret",
    username="your_username", 
    password="your_password",
    user_agent="Script Name v1.0 by /u/yourusername"
)

# Authentication is automatic - no additional steps needed
# All scopes are automatically granted for script apps

Web Application Authentication

For web applications that need user authorization with specific scopes.

# Step 1: Initialize Reddit instance for web app
reddit = praw.Reddit(
    client_id="your_client_id",
    client_secret="your_client_secret",
    redirect_uri="http://localhost:8080/callback",
    user_agent="Web App v1.0 by /u/yourusername"
)

# Step 2: Generate authorization URL
scopes = ["identity", "read", "submit", "edit"]
state = "random_string_for_csrf_protection"
auth_url = reddit.auth.url(scopes, state, duration="permanent")

# Step 3: Redirect user to auth_url, they authorize and return with code

# Step 4: Exchange code for tokens
reddit.auth.authorize(code_from_callback)

# Step 5: Reddit instance is now authenticated
authenticated_user = reddit.user.me()

Read-Only Authentication

For applications that only need to read public data.

# Read-only mode (no user authentication required)
reddit = praw.Reddit(
    client_id="your_client_id",
    client_secret="your_client_secret",
    user_agent="Read-Only App v1.0 by /u/yourusername"
)

# Enable read-only mode
reddit.read_only = True

# Can now access public content without user authentication
subreddit = reddit.subreddit("python")
for post in subreddit.hot(limit=10):
    print(post.title)

OAuth 2.0 Scopes

Reddit OAuth scopes define what actions your application can perform.

# Available OAuth scopes
SCOPES = {
    "identity": "Access user identity (username, karma, creation date)",
    "edit": "Edit and delete user's comments and submissions", 
    "flair": "Manage user and link flair",
    "history": "Access user's post and comment history",
    "modconfig": "Manage configuration and sidebar of subreddits",
    "modflair": "Manage flair templates and flair of other users",
    "modlog": "Access moderation log",
    "modposts": "Approve, remove, mark nsfw, and distinguish content",
    "modwiki": "Change wiki settings and edit wiki pages",
    "mysubreddits": "Access user's subscribed subreddits",
    "privatemessages": "Access and send private messages",
    "read": "Read posts and comments",
    "report": "Report content for rule violations",
    "save": "Save and unsave posts and comments",
    "submit": "Submit posts and comments", 
    "subscribe": "Subscribe and unsubscribe from subreddits",
    "vote": "Vote on posts and comments",
    "wikiedit": "Edit wiki pages",
    "wikiread": "Read wiki pages"
}

# Example: Request multiple scopes
scopes = ["identity", "read", "submit", "edit", "vote"]
auth_url = reddit.auth.url(scopes, state)

Token Management

Refresh Token Handling

PRAW automatically handles token refresh when using permanent tokens.

# Automatic token refresh (handled internally by PRAW)
# When access token expires, PRAW uses refresh token automatically

# Check current scopes
current_scopes = reddit.auth.scopes()
print(f"Authorized scopes: {current_scopes}")

# Manually revoke tokens if needed
reddit.auth.revoke_token()  # Revoke current access token
reddit.auth.revoke_token(revoke_refresh=True)  # Also revoke refresh token

Custom Token Managers

Implement custom token storage and retrieval mechanisms.

class BaseTokenManager:
    """Abstract base class for token managers."""
    
    def post_refresh_callback(self, authorizer):
        """Called after token refresh."""
        pass
    
    def pre_refresh_callback(self, authorizer):
        """Called before token refresh."""
        pass

class FileTokenManager(BaseTokenManager):
    """File-based token storage."""
    
    def __init__(self, filename: str): ...

class SQLiteTokenManager(BaseTokenManager):
    """SQLite-based token storage."""
    
    def __init__(self, database: str, key: str): ...

# Use custom token manager
token_manager = FileTokenManager("tokens.json")
reddit = praw.Reddit(
    client_id="your_client_id",
    client_secret="your_client_secret",
    redirect_uri="your_redirect_uri",
    user_agent="your_user_agent",
    token_manager=token_manager
)

Configuration Management

Environment Variables

Configure authentication using environment variables for security.

# Set environment variables
# praw_client_id=your_client_id
# praw_client_secret=your_client_secret  
# praw_username=your_username
# praw_password=your_password
# praw_user_agent=your_user_agent

# PRAW automatically uses environment variables
reddit = praw.Reddit()

# Or specify which environment variables to use
reddit = praw.Reddit(
    client_id=os.environ["MY_CLIENT_ID"],
    client_secret=os.environ["MY_CLIENT_SECRET"],
    username=os.environ["MY_USERNAME"], 
    password=os.environ["MY_PASSWORD"],
    user_agent=os.environ["MY_USER_AGENT"]
)

Configuration Files

Use praw.ini configuration files for different environments.

# praw.ini file format
"""
[DEFAULT]
user_agent=MyBot v1.0 by /u/yourusername

[development]
client_id=dev_client_id
client_secret=dev_client_secret
username=dev_username
password=dev_password

[production] 
client_id=prod_client_id
client_secret=prod_client_secret
username=prod_username
password=prod_password
"""

# Load specific configuration
reddit = praw.Reddit("development")  # Uses [development] section
reddit = praw.Reddit("production")   # Uses [production] section

# Override specific settings
reddit = praw.Reddit(
    "development",
    username="override_username"  # Override just username
)

Authentication State Management

Check authentication status and handle authentication errors.

# Check if authenticated
try:
    user = reddit.user.me()
    print(f"Authenticated as: {user.name}")
    is_authenticated = True
except AttributeError:
    print("Not authenticated")
    is_authenticated = False

# Check read-only mode
if reddit.read_only:
    print("In read-only mode")
else:
    print("In authenticated mode")

# Get current scopes
if not reddit.read_only:
    scopes = reddit.auth.scopes()
    print(f"Available scopes: {scopes}")
    
    # Check for specific scope
    if "submit" in scopes:
        print("Can submit posts")
    if "modposts" in scopes:
        print("Can moderate posts")

Error Handling

Handle authentication-related errors appropriately.

from praw.exceptions import (
    InvalidImplicitAuth,
    ReadOnlyException,
    RedditAPIException
)

try:
    # Attempt authenticated operation
    reddit.subreddit("test").submit("title", selftext="content")
    
except ReadOnlyException:
    print("Cannot submit in read-only mode")
    
except InvalidImplicitAuth:
    print("Invalid implicit authentication")
    
except RedditAPIException as e:
    # Handle specific Reddit API errors
    for error in e.items:
        if error.error_type == "NO_PERMISSION":
            print("Insufficient permissions")
        elif error.error_type == "INVALID_CREDENTIALS":
            print("Invalid authentication credentials")

Usage Examples

Script Authentication Setup

import praw
import os

# Method 1: Direct configuration
reddit = praw.Reddit(
    client_id="your_client_id",
    client_secret="your_client_secret",
    username="your_username",
    password="your_password", 
    user_agent="MyScript v1.0 by /u/yourusername"
)

# Method 2: Environment variables
reddit = praw.Reddit(
    client_id=os.environ["PRAW_CLIENT_ID"],
    client_secret=os.environ["PRAW_CLIENT_SECRET"],
    username=os.environ["PRAW_USERNAME"],
    password=os.environ["PRAW_PASSWORD"],
    user_agent=os.environ["PRAW_USER_AGENT"]
)

# Verify authentication
me = reddit.user.me()
print(f"Authenticated as: {me.name}")

Web Application Authentication

import praw
from flask import Flask, request, redirect, session
import secrets

app = Flask(__name__)
app.secret_key = "your-secret-key"

reddit = praw.Reddit(
    client_id="your_client_id",
    client_secret="your_client_secret", 
    redirect_uri="http://localhost:5000/callback",
    user_agent="WebApp v1.0 by /u/yourusername"
)

@app.route("/login")
def login():
    # Generate state for CSRF protection
    state = secrets.token_urlsafe(32)
    session["oauth_state"] = state
    
    # Request scopes
    scopes = ["identity", "read", "submit"]
    
    # Generate authorization URL
    auth_url = reddit.auth.url(scopes, state, duration="permanent")
    return redirect(auth_url)

@app.route("/callback")
def callback():
    # Verify state parameter
    if request.args.get("state") != session.get("oauth_state"):
        return "Invalid state parameter", 400
    
    # Get authorization code
    code = request.args.get("code")
    if not code:
        return "No authorization code received", 400
    
    # Exchange code for tokens
    reddit.auth.authorize(code)
    
    # Get authenticated user
    user = reddit.user.me()
    session["reddit_username"] = user.name
    
    return f"Successfully authenticated as {user.name}"

@app.route("/protected")  
def protected():
    if "reddit_username" not in session:
        return redirect("/login")
    
    # Use authenticated Reddit instance
    username = session["reddit_username"] 
    user = reddit.redditor(username)
    
    return f"Hello {username}! Your karma: {user.comment_karma}"

Token Management Example

import praw
from praw.util.token_manager import FileTokenManager

# Use file-based token storage
token_manager = FileTokenManager("reddit_tokens.json")

reddit = praw.Reddit(
    client_id="your_client_id",
    client_secret="your_client_secret",
    redirect_uri="your_redirect_uri", 
    user_agent="your_user_agent",
    token_manager=token_manager
)

# First time: perform OAuth flow
if not hasattr(reddit.auth, 'refresh_token'):
    scopes = ["identity", "read", "submit"]
    state = "random_state"
    auth_url = reddit.auth.url(scopes, state)
    print(f"Visit: {auth_url}")
    
    code = input("Enter authorization code: ")
    reddit.auth.authorize(code)

# Subsequent runs: tokens are loaded automatically
user = reddit.user.me()
print(f"Authenticated as: {user.name}")

# Check authorized scopes
scopes = reddit.auth.scopes()
print(f"Authorized scopes: {scopes}")

# Revoke tokens when done
reddit.auth.revoke_token(revoke_refresh=True)

Types

class Config:
    """PRAW configuration management."""
    
    CONFIG_NOT_SET: str  # Sentinel value for unset configuration
    
    def __init__(
        self,
        site_name: str = None,
        interpolation: str = None,
        **settings
    ): ...

class InvalidImplicitAuth(Exception):
    """Raised when implicit authentication is used incorrectly."""
    
class ReadOnlyException(Exception): 
    """Raised when attempting write operations in read-only mode."""
    
class InvalidURL(Exception):
    """Raised when an invalid URL is encountered."""

class MissingRequiredAttributeException(Exception):
    """Raised when required configuration is missing."""

Install with Tessl CLI

npx tessl i tessl/pypi-praw

docs

authentication.md

content-discovery.md

index.md

live-threads.md

messaging-inbox.md

moderation.md

reddit-client.md

submissions-comments.md

subreddits.md

user-management.md

tile.json