or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdcontent-discovery.mdindex.mdlive-threads.mdmessaging-inbox.mdmoderation.mdreddit-client.mdsubmissions-comments.mdsubreddits.mduser-management.md

authentication.mddocs/

0

# Authentication

1

2

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.

3

4

## Capabilities

5

6

### Auth Class - Authentication Management

7

8

Handle OAuth 2.0 authentication flows and token management.

9

10

```python { .api }

11

class Auth:

12

def __init__(self, reddit): ...

13

14

def authorize(self, code: str):

15

"""

16

Complete OAuth 2.0 authorization with authorization code.

17

18

Parameters:

19

- code: Authorization code from Reddit OAuth callback

20

21

This method exchanges the authorization code for access and refresh tokens.

22

Used in web application authentication flow.

23

"""

24

25

def implicit(

26

self,

27

access_token: str,

28

expires_in: int,

29

scope: str

30

):

31

"""

32

Set implicit OAuth 2.0 authorization.

33

34

Parameters:

35

- access_token: Access token from implicit flow

36

- expires_in: Token expiration time in seconds

37

- scope: Space-separated list of granted scopes

38

39

Used for implicit grant flow (less secure, not recommended).

40

"""

41

42

def url(

43

self,

44

scopes: list,

45

state: str,

46

*,

47

duration: str = "temporary",

48

implicit: bool = False,

49

**kwargs

50

) -> str:

51

"""

52

Generate OAuth 2.0 authorization URL.

53

54

Parameters:

55

- scopes: List of requested scopes

56

- state: Random string to prevent CSRF attacks

57

- duration: "temporary" or "permanent" token duration

58

- implicit: Use implicit grant flow

59

60

Returns:

61

Authorization URL for user to visit

62

"""

63

64

def revoke_token(

65

self,

66

token: str = None,

67

*,

68

revoke_refresh: bool = True,

69

**kwargs

70

):

71

"""

72

Revoke access or refresh token.

73

74

Parameters:

75

- token: Token to revoke (defaults to current access token)

76

- revoke_refresh: Also revoke refresh token

77

78

Invalidates the specified token on Reddit's servers.

79

"""

80

81

def scopes(self) -> set:

82

"""

83

Get currently authorized scopes.

84

85

Returns:

86

Set of scope strings currently granted

87

"""

88

```

89

90

### Authentication Flows

91

92

#### Script Application Authentication

93

94

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

95

96

```python { .api }

97

# Script authentication configuration

98

reddit = praw.Reddit(

99

client_id="your_client_id",

100

client_secret="your_client_secret",

101

username="your_username",

102

password="your_password",

103

user_agent="Script Name v1.0 by /u/yourusername"

104

)

105

106

# Authentication is automatic - no additional steps needed

107

# All scopes are automatically granted for script apps

108

```

109

110

#### Web Application Authentication

111

112

For web applications that need user authorization with specific scopes.

113

114

```python { .api }

115

# Step 1: Initialize Reddit instance for web app

116

reddit = praw.Reddit(

117

client_id="your_client_id",

118

client_secret="your_client_secret",

119

redirect_uri="http://localhost:8080/callback",

120

user_agent="Web App v1.0 by /u/yourusername"

121

)

122

123

# Step 2: Generate authorization URL

124

scopes = ["identity", "read", "submit", "edit"]

125

state = "random_string_for_csrf_protection"

126

auth_url = reddit.auth.url(scopes, state, duration="permanent")

127

128

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

129

130

# Step 4: Exchange code for tokens

131

reddit.auth.authorize(code_from_callback)

132

133

# Step 5: Reddit instance is now authenticated

134

authenticated_user = reddit.user.me()

135

```

136

137

#### Read-Only Authentication

138

139

For applications that only need to read public data.

140

141

```python { .api }

142

# Read-only mode (no user authentication required)

143

reddit = praw.Reddit(

144

client_id="your_client_id",

145

client_secret="your_client_secret",

146

user_agent="Read-Only App v1.0 by /u/yourusername"

147

)

148

149

# Enable read-only mode

150

reddit.read_only = True

151

152

# Can now access public content without user authentication

153

subreddit = reddit.subreddit("python")

154

for post in subreddit.hot(limit=10):

155

print(post.title)

156

```

157

158

### OAuth 2.0 Scopes

159

160

Reddit OAuth scopes define what actions your application can perform.

161

162

```python { .api }

163

# Available OAuth scopes

164

SCOPES = {

165

"identity": "Access user identity (username, karma, creation date)",

166

"edit": "Edit and delete user's comments and submissions",

167

"flair": "Manage user and link flair",

168

"history": "Access user's post and comment history",

169

"modconfig": "Manage configuration and sidebar of subreddits",

170

"modflair": "Manage flair templates and flair of other users",

171

"modlog": "Access moderation log",

172

"modposts": "Approve, remove, mark nsfw, and distinguish content",

173

"modwiki": "Change wiki settings and edit wiki pages",

174

"mysubreddits": "Access user's subscribed subreddits",

175

"privatemessages": "Access and send private messages",

176

"read": "Read posts and comments",

177

"report": "Report content for rule violations",

178

"save": "Save and unsave posts and comments",

179

"submit": "Submit posts and comments",

180

"subscribe": "Subscribe and unsubscribe from subreddits",

181

"vote": "Vote on posts and comments",

182

"wikiedit": "Edit wiki pages",

183

"wikiread": "Read wiki pages"

184

}

185

186

# Example: Request multiple scopes

187

scopes = ["identity", "read", "submit", "edit", "vote"]

188

auth_url = reddit.auth.url(scopes, state)

189

```

190

191

### Token Management

192

193

#### Refresh Token Handling

194

195

PRAW automatically handles token refresh when using permanent tokens.

196

197

```python { .api }

198

# Automatic token refresh (handled internally by PRAW)

199

# When access token expires, PRAW uses refresh token automatically

200

201

# Check current scopes

202

current_scopes = reddit.auth.scopes()

203

print(f"Authorized scopes: {current_scopes}")

204

205

# Manually revoke tokens if needed

206

reddit.auth.revoke_token() # Revoke current access token

207

reddit.auth.revoke_token(revoke_refresh=True) # Also revoke refresh token

208

```

209

210

#### Custom Token Managers

211

212

Implement custom token storage and retrieval mechanisms.

213

214

```python { .api }

215

class BaseTokenManager:

216

"""Abstract base class for token managers."""

217

218

def post_refresh_callback(self, authorizer):

219

"""Called after token refresh."""

220

pass

221

222

def pre_refresh_callback(self, authorizer):

223

"""Called before token refresh."""

224

pass

225

226

class FileTokenManager(BaseTokenManager):

227

"""File-based token storage."""

228

229

def __init__(self, filename: str): ...

230

231

class SQLiteTokenManager(BaseTokenManager):

232

"""SQLite-based token storage."""

233

234

def __init__(self, database: str, key: str): ...

235

236

# Use custom token manager

237

token_manager = FileTokenManager("tokens.json")

238

reddit = praw.Reddit(

239

client_id="your_client_id",

240

client_secret="your_client_secret",

241

redirect_uri="your_redirect_uri",

242

user_agent="your_user_agent",

243

token_manager=token_manager

244

)

245

```

246

247

### Configuration Management

248

249

#### Environment Variables

250

251

Configure authentication using environment variables for security.

252

253

```python { .api }

254

# Set environment variables

255

# praw_client_id=your_client_id

256

# praw_client_secret=your_client_secret

257

# praw_username=your_username

258

# praw_password=your_password

259

# praw_user_agent=your_user_agent

260

261

# PRAW automatically uses environment variables

262

reddit = praw.Reddit()

263

264

# Or specify which environment variables to use

265

reddit = praw.Reddit(

266

client_id=os.environ["MY_CLIENT_ID"],

267

client_secret=os.environ["MY_CLIENT_SECRET"],

268

username=os.environ["MY_USERNAME"],

269

password=os.environ["MY_PASSWORD"],

270

user_agent=os.environ["MY_USER_AGENT"]

271

)

272

```

273

274

#### Configuration Files

275

276

Use praw.ini configuration files for different environments.

277

278

```python { .api }

279

# praw.ini file format

280

"""

281

[DEFAULT]

282

user_agent=MyBot v1.0 by /u/yourusername

283

284

[development]

285

client_id=dev_client_id

286

client_secret=dev_client_secret

287

username=dev_username

288

password=dev_password

289

290

[production]

291

client_id=prod_client_id

292

client_secret=prod_client_secret

293

username=prod_username

294

password=prod_password

295

"""

296

297

# Load specific configuration

298

reddit = praw.Reddit("development") # Uses [development] section

299

reddit = praw.Reddit("production") # Uses [production] section

300

301

# Override specific settings

302

reddit = praw.Reddit(

303

"development",

304

username="override_username" # Override just username

305

)

306

```

307

308

### Authentication State Management

309

310

Check authentication status and handle authentication errors.

311

312

```python { .api }

313

# Check if authenticated

314

try:

315

user = reddit.user.me()

316

print(f"Authenticated as: {user.name}")

317

is_authenticated = True

318

except AttributeError:

319

print("Not authenticated")

320

is_authenticated = False

321

322

# Check read-only mode

323

if reddit.read_only:

324

print("In read-only mode")

325

else:

326

print("In authenticated mode")

327

328

# Get current scopes

329

if not reddit.read_only:

330

scopes = reddit.auth.scopes()

331

print(f"Available scopes: {scopes}")

332

333

# Check for specific scope

334

if "submit" in scopes:

335

print("Can submit posts")

336

if "modposts" in scopes:

337

print("Can moderate posts")

338

```

339

340

### Error Handling

341

342

Handle authentication-related errors appropriately.

343

344

```python { .api }

345

from praw.exceptions import (

346

InvalidImplicitAuth,

347

ReadOnlyException,

348

RedditAPIException

349

)

350

351

try:

352

# Attempt authenticated operation

353

reddit.subreddit("test").submit("title", selftext="content")

354

355

except ReadOnlyException:

356

print("Cannot submit in read-only mode")

357

358

except InvalidImplicitAuth:

359

print("Invalid implicit authentication")

360

361

except RedditAPIException as e:

362

# Handle specific Reddit API errors

363

for error in e.items:

364

if error.error_type == "NO_PERMISSION":

365

print("Insufficient permissions")

366

elif error.error_type == "INVALID_CREDENTIALS":

367

print("Invalid authentication credentials")

368

```

369

370

## Usage Examples

371

372

### Script Authentication Setup

373

374

```python

375

import praw

376

import os

377

378

# Method 1: Direct configuration

379

reddit = praw.Reddit(

380

client_id="your_client_id",

381

client_secret="your_client_secret",

382

username="your_username",

383

password="your_password",

384

user_agent="MyScript v1.0 by /u/yourusername"

385

)

386

387

# Method 2: Environment variables

388

reddit = praw.Reddit(

389

client_id=os.environ["PRAW_CLIENT_ID"],

390

client_secret=os.environ["PRAW_CLIENT_SECRET"],

391

username=os.environ["PRAW_USERNAME"],

392

password=os.environ["PRAW_PASSWORD"],

393

user_agent=os.environ["PRAW_USER_AGENT"]

394

)

395

396

# Verify authentication

397

me = reddit.user.me()

398

print(f"Authenticated as: {me.name}")

399

```

400

401

### Web Application Authentication

402

403

```python

404

import praw

405

from flask import Flask, request, redirect, session

406

import secrets

407

408

app = Flask(__name__)

409

app.secret_key = "your-secret-key"

410

411

reddit = praw.Reddit(

412

client_id="your_client_id",

413

client_secret="your_client_secret",

414

redirect_uri="http://localhost:5000/callback",

415

user_agent="WebApp v1.0 by /u/yourusername"

416

)

417

418

@app.route("/login")

419

def login():

420

# Generate state for CSRF protection

421

state = secrets.token_urlsafe(32)

422

session["oauth_state"] = state

423

424

# Request scopes

425

scopes = ["identity", "read", "submit"]

426

427

# Generate authorization URL

428

auth_url = reddit.auth.url(scopes, state, duration="permanent")

429

return redirect(auth_url)

430

431

@app.route("/callback")

432

def callback():

433

# Verify state parameter

434

if request.args.get("state") != session.get("oauth_state"):

435

return "Invalid state parameter", 400

436

437

# Get authorization code

438

code = request.args.get("code")

439

if not code:

440

return "No authorization code received", 400

441

442

# Exchange code for tokens

443

reddit.auth.authorize(code)

444

445

# Get authenticated user

446

user = reddit.user.me()

447

session["reddit_username"] = user.name

448

449

return f"Successfully authenticated as {user.name}"

450

451

@app.route("/protected")

452

def protected():

453

if "reddit_username" not in session:

454

return redirect("/login")

455

456

# Use authenticated Reddit instance

457

username = session["reddit_username"]

458

user = reddit.redditor(username)

459

460

return f"Hello {username}! Your karma: {user.comment_karma}"

461

```

462

463

### Token Management Example

464

465

```python

466

import praw

467

from praw.util.token_manager import FileTokenManager

468

469

# Use file-based token storage

470

token_manager = FileTokenManager("reddit_tokens.json")

471

472

reddit = praw.Reddit(

473

client_id="your_client_id",

474

client_secret="your_client_secret",

475

redirect_uri="your_redirect_uri",

476

user_agent="your_user_agent",

477

token_manager=token_manager

478

)

479

480

# First time: perform OAuth flow

481

if not hasattr(reddit.auth, 'refresh_token'):

482

scopes = ["identity", "read", "submit"]

483

state = "random_state"

484

auth_url = reddit.auth.url(scopes, state)

485

print(f"Visit: {auth_url}")

486

487

code = input("Enter authorization code: ")

488

reddit.auth.authorize(code)

489

490

# Subsequent runs: tokens are loaded automatically

491

user = reddit.user.me()

492

print(f"Authenticated as: {user.name}")

493

494

# Check authorized scopes

495

scopes = reddit.auth.scopes()

496

print(f"Authorized scopes: {scopes}")

497

498

# Revoke tokens when done

499

reddit.auth.revoke_token(revoke_refresh=True)

500

```

501

502

## Types

503

504

```python { .api }

505

class Config:

506

"""PRAW configuration management."""

507

508

CONFIG_NOT_SET: str # Sentinel value for unset configuration

509

510

def __init__(

511

self,

512

site_name: str = None,

513

interpolation: str = None,

514

**settings

515

): ...

516

517

class InvalidImplicitAuth(Exception):

518

"""Raised when implicit authentication is used incorrectly."""

519

520

class ReadOnlyException(Exception):

521

"""Raised when attempting write operations in read-only mode."""

522

523

class InvalidURL(Exception):

524

"""Raised when an invalid URL is encountered."""

525

526

class MissingRequiredAttributeException(Exception):

527

"""Raised when required configuration is missing."""

528

```