or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

drf-integration.mdindex.mdmanagement-commands.mdmanagement-views.mdmodels.mdoauth2-endpoints.mdoidc.mdsettings.mdview-protection.md

oauth2-endpoints.mddocs/

0

# OAuth2 Endpoints

1

2

Django OAuth Toolkit provides complete OAuth2 server endpoints implementing RFC 6749 and related specifications. These views handle authorization, token issuance, revocation, and introspection with support for all standard OAuth2 flows.

3

4

## Capabilities

5

6

### Authorization Endpoint

7

8

OAuth2 authorization endpoint that handles authorization requests and user consent.

9

10

```python { .api }

11

class AuthorizationView(BaseAuthorizationView):

12

"""

13

OAuth2 authorization endpoint (/o/authorize/).

14

15

Handles authorization requests for all grant types:

16

- Authorization Code Grant

17

- Implicit Grant

18

- OpenID Connect Hybrid Flow

19

20

Methods:

21

GET: Display authorization form to user

22

POST: Process user authorization decision

23

24

Query Parameters:

25

response_type: 'code', 'token', or 'id_token' combinations

26

client_id: OAuth2 client identifier

27

redirect_uri: Where to redirect after authorization

28

scope: Requested OAuth2 scopes

29

state: Client state parameter

30

nonce: OIDC nonce for ID token

31

code_challenge: PKCE code challenge

32

code_challenge_method: PKCE challenge method ('plain' or 'S256')

33

claims: OIDC claims parameter

34

35

Returns:

36

Authorization form or redirect to client with code/token

37

"""

38

39

template_name = "oauth2_provider/authorize.html"

40

form_class = AllowForm

41

42

def get(self, request, *args, **kwargs):

43

"""Display authorization form"""

44

45

def post(self, request, *args, **kwargs):

46

"""Process authorization decision"""

47

```

48

49

### Token Endpoint

50

51

OAuth2 token endpoint for issuing and refreshing access tokens.

52

53

```python { .api }

54

class TokenView(BaseTokenView):

55

"""

56

OAuth2 token endpoint (/o/token/).

57

58

Handles token requests for all grant types:

59

- Authorization Code Grant

60

- Resource Owner Password Credentials Grant

61

- Client Credentials Grant

62

- Refresh Token Grant

63

64

Methods:

65

POST: Issue or refresh access tokens

66

67

Form Parameters:

68

grant_type: Type of grant being used

69

code: Authorization code (authorization_code grant)

70

redirect_uri: Redirect URI used in authorization

71

username: Resource owner username (password grant)

72

password: Resource owner password (password grant)

73

refresh_token: Refresh token (refresh_token grant)

74

scope: Requested scopes

75

client_id: OAuth2 client identifier

76

client_secret: OAuth2 client secret

77

code_verifier: PKCE code verifier

78

79

Returns:

80

JSON response with access_token, token_type, expires_in, refresh_token, scope

81

"""

82

83

def post(self, request, *args, **kwargs):

84

"""Process token request and return JSON response"""

85

```

86

87

### Token Revocation Endpoint

88

89

OAuth2 token revocation endpoint implementing RFC 7009.

90

91

```python { .api }

92

class RevokeTokenView(BaseRevokeTokenView):

93

"""

94

OAuth2 token revocation endpoint (/o/revoke_token/).

95

96

Revokes access tokens and refresh tokens as per RFC 7009.

97

98

Methods:

99

POST: Revoke specified token

100

101

Form Parameters:

102

token: The token to revoke (access or refresh token)

103

token_type_hint: Hint about token type ('access_token' or 'refresh_token')

104

client_id: OAuth2 client identifier

105

client_secret: OAuth2 client secret (for confidential clients)

106

107

Returns:

108

HTTP 200 on successful revocation

109

HTTP 400 for invalid requests

110

"""

111

112

def post(self, request, *args, **kwargs):

113

"""Revoke the specified token"""

114

```

115

116

### Token Introspection Endpoint

117

118

OAuth2 token introspection endpoint implementing RFC 7662.

119

120

```python { .api }

121

class IntrospectTokenView(BaseIntrospectTokenView):

122

"""

123

OAuth2 token introspection endpoint (/o/introspect/).

124

125

Provides metadata about OAuth2 tokens as per RFC 7662.

126

127

Methods:

128

POST: Return token metadata

129

130

Form Parameters:

131

token: The token to introspect

132

token_type_hint: Hint about token type

133

client_id: OAuth2 client identifier

134

client_secret: OAuth2 client secret

135

136

Returns:

137

JSON response with token metadata:

138

- active: Boolean indicating if token is active

139

- scope: Space-separated list of scopes

140

- client_id: Client identifier

141

- username: Resource owner username

142

- token_type: Type of token

143

- exp: Expiration timestamp

144

- iat: Issued at timestamp

145

- sub: Subject identifier

146

- aud: Audience

147

- iss: Issuer

148

"""

149

150

def post(self, request, *args, **kwargs):

151

"""Return token introspection data"""

152

```

153

154

## URL Patterns

155

156

### Base OAuth2 URLs

157

158

Core OAuth2 endpoint URL patterns that should be included in your Django URL configuration.

159

160

```python { .api }

161

base_urlpatterns = [

162

path("authorize/", views.AuthorizationView.as_view(), name="authorize"),

163

path("token/", views.TokenView.as_view(), name="token"),

164

path("revoke_token/", views.RevokeTokenView.as_view(), name="revoke-token"),

165

path("introspect/", views.IntrospectTokenView.as_view(), name="introspect"),

166

]

167

```

168

169

## Usage Examples

170

171

### URL Configuration

172

173

```python

174

# urls.py

175

from django.urls import path, include

176

177

urlpatterns = [

178

# Include OAuth2 URLs

179

path('o/', include('oauth2_provider.urls', namespace='oauth2_provider')),

180

# Your app URLs

181

path('api/', include('myapp.urls')),

182

]

183

```

184

185

### Authorization Code Flow

186

187

```python

188

# Client initiates authorization request

189

# GET /o/authorize/?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=read+write&state=STATE

190

191

# After user authorization, client receives:

192

# HTTP/1.1 302 Found

193

# Location: REDIRECT_URI?code=AUTHORIZATION_CODE&state=STATE

194

195

# Client exchanges code for token

196

# POST /o/token/

197

# Content-Type: application/x-www-form-urlencoded

198

#

199

# grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=REDIRECT_URI&client_id=CLIENT_ID&client_secret=CLIENT_SECRET

200

201

# Response:

202

# {

203

# "access_token": "ACCESS_TOKEN",

204

# "token_type": "Bearer",

205

# "expires_in": 3600,

206

# "refresh_token": "REFRESH_TOKEN",

207

# "scope": "read write"

208

# }

209

```

210

211

### Client Credentials Flow

212

213

```python

214

# Server-to-server authentication

215

# POST /o/token/

216

# Content-Type: application/x-www-form-urlencoded

217

# Authorization: Basic BASE64(CLIENT_ID:CLIENT_SECRET)

218

#

219

# grant_type=client_credentials&scope=api

220

221

# Response:

222

# {

223

# "access_token": "ACCESS_TOKEN",

224

# "token_type": "Bearer",

225

# "expires_in": 3600,

226

# "scope": "api"

227

# }

228

```

229

230

### Resource Owner Password Credentials Flow

231

232

```python

233

# Direct username/password authentication (use carefully)

234

# POST /o/token/

235

# Content-Type: application/x-www-form-urlencoded

236

#

237

# grant_type=password&username=USERNAME&password=PASSWORD&client_id=CLIENT_ID&client_secret=CLIENT_SECRET&scope=read+write

238

239

# Response:

240

# {

241

# "access_token": "ACCESS_TOKEN",

242

# "token_type": "Bearer",

243

# "expires_in": 3600,

244

# "refresh_token": "REFRESH_TOKEN",

245

# "scope": "read write"

246

# }

247

```

248

249

### Refresh Token Flow

250

251

```python

252

# Refresh expired access token

253

# POST /o/token/

254

# Content-Type: application/x-www-form-urlencoded

255

#

256

# grant_type=refresh_token&refresh_token=REFRESH_TOKEN&client_id=CLIENT_ID&client_secret=CLIENT_SECRET

257

258

# Response:

259

# {

260

# "access_token": "NEW_ACCESS_TOKEN",

261

# "token_type": "Bearer",

262

# "expires_in": 3600,

263

# "refresh_token": "NEW_REFRESH_TOKEN",

264

# "scope": "read write"

265

# }

266

```

267

268

### Token Revocation

269

270

```python

271

# Revoke access token

272

# POST /o/revoke_token/

273

# Content-Type: application/x-www-form-urlencoded

274

# Authorization: Basic BASE64(CLIENT_ID:CLIENT_SECRET)

275

#

276

# token=ACCESS_TOKEN&token_type_hint=access_token

277

278

# Response: HTTP 200 OK (empty body)

279

280

# Revoke refresh token (also revokes associated access tokens)

281

# POST /o/revoke_token/

282

# Content-Type: application/x-www-form-urlencoded

283

#

284

# token=REFRESH_TOKEN&token_type_hint=refresh_token&client_id=CLIENT_ID&client_secret=CLIENT_SECRET

285

```

286

287

### Token Introspection

288

289

```python

290

# Introspect token for metadata

291

# POST /o/introspect/

292

# Content-Type: application/x-www-form-urlencoded

293

# Authorization: Basic BASE64(CLIENT_ID:CLIENT_SECRET)

294

#

295

# token=ACCESS_TOKEN

296

297

# Active token response:

298

# {

299

# "active": true,

300

# "scope": "read write",

301

# "client_id": "client123",

302

# "username": "user@example.com",

303

# "token_type": "Bearer",

304

# "exp": 1640995200,

305

# "iat": 1640991600,

306

# "sub": "user@example.com",

307

# "aud": "client123"

308

# }

309

310

# Inactive token response:

311

# {

312

# "active": false

313

# }

314

```

315

316

### PKCE (Proof Key for Code Exchange)

317

318

```python

319

# Authorization request with PKCE

320

# GET /o/authorize/?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=read&code_challenge=CODE_CHALLENGE&code_challenge_method=S256

321

322

# Token exchange with code verifier

323

# POST /o/token/

324

# Content-Type: application/x-www-form-urlencoded

325

#

326

# grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=REDIRECT_URI&client_id=CLIENT_ID&code_verifier=CODE_VERIFIER

327

```

328

329

### Custom Views

330

331

```python

332

from oauth2_provider.views.base import BaseAuthorizationView

333

from django.shortcuts import render

334

335

class CustomAuthorizationView(BaseAuthorizationView):

336

"""Custom authorization view with custom template"""

337

338

template_name = "myapp/custom_authorize.html"

339

340

def get_context_data(self, **kwargs):

341

context = super().get_context_data(**kwargs)

342

# Add custom context

343

context['custom_data'] = 'Custom authorization page'

344

return context

345

346

# Use in URL configuration

347

# path('custom/authorize/', CustomAuthorizationView.as_view(), name='custom_authorize')

348

```

349

350

### Error Handling

351

352

OAuth2 endpoints return standard error responses following RFC 6749:

353

354

```python

355

# Authorization endpoint errors (redirect to client):

356

# error=invalid_request&error_description=Missing+client_id+parameter

357

# error=unauthorized_client&error_description=Client+not+authorized

358

# error=access_denied&error_description=User+denied+authorization

359

# error=unsupported_response_type&error_description=Response+type+not+supported

360

# error=invalid_scope&error_description=Requested+scope+invalid

361

# error=server_error&error_description=Server+encountered+error

362

363

# Token endpoint errors (JSON response):

364

# {

365

# "error": "invalid_request",

366

# "error_description": "Missing grant_type parameter"

367

# }

368

#

369

# {

370

# "error": "invalid_client",

371

# "error_description": "Client authentication failed"

372

# }

373

#

374

# {

375

# "error": "invalid_grant",

376

# "error_description": "Authorization code has expired"

377

# }

378

#

379

# {

380

# "error": "unsupported_grant_type",

381

# "error_description": "Grant type not supported"

382

# }

383

```