or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

admin-interface.mdcore-authentication.mddevice-models.mddjango-integration.mdemail-devices.mdhotp-devices.mdindex.mdoath-algorithms.mdstatic-tokens.mdtotp-devices.md

django-integration.mddocs/

0

# Django Integration

1

2

Forms, views, middleware, and decorators for integrating OTP authentication into Django applications. These components provide seamless integration with Django's built-in authentication system.

3

4

## Capabilities

5

6

### Forms

7

8

#### OTPAuthenticationForm

9

10

Complete authentication form with username, password, and OTP fields.

11

12

```python { .api }

13

class OTPAuthenticationForm(OTPAuthenticationFormMixin, AuthenticationForm):

14

"""

15

Complete OTP authentication form with username/password/token.

16

17

Fields:

18

- username: CharField - User identification

19

- password: CharField - User password

20

- otp_device: CharField - Device selection

21

- otp_token: CharField - OTP token input

22

- otp_challenge: CharField - Challenge generation button

23

"""

24

25

otp_device = forms.CharField(widget=forms.Select)

26

otp_token = forms.CharField(required=False, widget=forms.TextInput)

27

otp_challenge = forms.CharField(required=False, widget=forms.TextInput)

28

```

29

30

#### OTPTokenForm

31

32

Token verification form for already authenticated users.

33

34

```python { .api }

35

class OTPTokenForm(OTPAuthenticationFormMixin, forms.Form):

36

"""

37

Token verification form for authenticated users.

38

39

Fields:

40

- otp_device: ChoiceField - Device selection

41

- otp_token: CharField - OTP token input

42

- otp_challenge: CharField - Challenge generation button

43

"""

44

45

otp_device = forms.ChoiceField()

46

otp_token = forms.CharField(required=False)

47

otp_challenge = forms.CharField(required=False)

48

49

def get_user(self):

50

"""Returns the authenticated user."""

51

```

52

53

#### OTPAuthenticationFormMixin

54

55

Shared functionality for OTP-aware authentication forms.

56

57

```python { .api }

58

class OTPAuthenticationFormMixin:

59

"""Shared functionality for OTP-aware authentication forms."""

60

61

otp_error_messages = {

62

'invalid_token': 'Invalid token. Please make sure you have entered it correctly.',

63

'token_required': 'Please enter your authentication code.',

64

# ... more error messages

65

}

66

67

def clean_otp(self, user):

68

"""Process OTP fields and verify token."""

69

70

@staticmethod

71

def device_choices(user):

72

"""Return device choices for user."""

73

```

74

75

### Views

76

77

#### LoginView

78

79

Two-factor authentication login view that handles both password and OTP verification.

80

81

```python { .api }

82

class LoginView(auth_views.LoginView):

83

"""Two-factor authentication login view."""

84

85

otp_authentication_form = OTPAuthenticationForm

86

otp_token_form = OTPTokenForm

87

88

@property

89

def authentication_form(self):

90

"""Dynamically select form class based on authentication state."""

91

92

def form_valid(self, form):

93

"""Handle successful form submission."""

94

```

95

96

#### login Function View

97

98

```python { .api }

99

def login(request, **kwargs):

100

"""Function-based view wrapper for LoginView."""

101

```

102

103

### Decorators

104

105

#### otp_required

106

107

Decorator that requires users to be verified by an OTP device.

108

109

```python { .api }

110

def otp_required(view=None, redirect_field_name='next', login_url=None, if_configured=False):

111

"""

112

Decorator requiring OTP verification.

113

114

Parameters:

115

- view: Function - View function (when used without parentheses)

116

- redirect_field_name: str - Field name for redirect URL

117

- login_url: str - URL to redirect to for authentication

118

- if_configured: bool - Allow users with no devices if True

119

120

Returns:

121

Decorator function or decorated view

122

"""

123

```

124

125

### Middleware

126

127

#### OTPMiddleware

128

129

Middleware that adds OTP device information to request.user.

130

131

```python { .api }

132

class OTPMiddleware:

133

"""Middleware that adds OTP device information to request.user."""

134

135

def __init__(self, get_response=None):

136

"""Initialize middleware."""

137

138

def __call__(self, request):

139

"""Process request and add OTP info to user."""

140

```

141

142

#### is_verified Function

143

144

```python { .api }

145

def is_verified(user) -> bool:

146

"""

147

Check if user is verified by OTP device.

148

149

Parameters:

150

- user: User - User instance to check

151

152

Returns:

153

bool - True if user is OTP-verified

154

"""

155

```

156

157

### Signals

158

159

```python { .api }

160

otp_verification_failed = django.dispatch.Signal()

161

```

162

163

Signal sent when OTP verification fails, providing hooks for logging or additional security measures.

164

165

## Usage Examples

166

167

### Basic View Integration

168

169

```python

170

from django_otp.decorators import otp_required

171

from django.shortcuts import render

172

173

@otp_required

174

def secure_view(request):

175

"""View requiring OTP verification."""

176

return render(request, 'secure_page.html')

177

178

@otp_required(if_configured=True)

179

def optional_otp_view(request):

180

"""View requiring OTP only if user has devices configured."""

181

return render(request, 'semi_secure_page.html')

182

```

183

184

### Custom Login Flow

185

186

```python

187

from django_otp.views import LoginView as OTPLoginView

188

from django.urls import reverse_lazy

189

190

class CustomOTPLoginView(OTPLoginView):

191

"""Custom login view with additional features."""

192

193

template_name = 'custom_login.html'

194

success_url = reverse_lazy('dashboard')

195

196

def form_valid(self, form):

197

"""Add custom logic after successful login."""

198

response = super().form_valid(form)

199

200

# Log successful OTP login

201

if hasattr(form, 'get_user') and hasattr(form.get_user(), 'otp_device'):

202

device = form.get_user().otp_device

203

print(f"User {form.get_user().username} logged in with {device.name}")

204

205

return response

206

```

207

208

### Middleware Usage

209

210

```python

211

# settings.py

212

MIDDLEWARE = [

213

# ... other middleware

214

'django_otp.middleware.OTPMiddleware',

215

# ... more middleware

216

]

217

218

# In views

219

from django_otp.middleware import is_verified

220

221

def my_view(request):

222

if is_verified(request.user):

223

# User is OTP-verified

224

return render(request, 'secure_content.html')

225

else:

226

# User needs OTP verification

227

return redirect('otp_login')

228

```

229

230

### Custom OTP Forms

231

232

```python

233

from django_otp.forms import OTPTokenForm

234

from django import forms

235

236

class CustomOTPForm(OTPTokenForm):

237

"""Custom OTP form with additional fields."""

238

239

remember_device = forms.BooleanField(

240

required=False,

241

label="Remember this device for 30 days"

242

)

243

244

def __init__(self, user, *args, **kwargs):

245

super().__init__(*args, **kwargs)

246

self.user = user

247

248

# Customize device choices

249

device_choices = []

250

for device in self.device_choices(user):

251

device_choices.append((device[0], f"{device[1]} ({device[0][:8]}...)"))

252

253

self.fields['otp_device'].choices = device_choices

254

```

255

256

### Signal Handling

257

258

```python

259

from django_otp.forms import otp_verification_failed

260

from django.dispatch import receiver

261

import logging

262

263

logger = logging.getLogger(__name__)

264

265

@receiver(otp_verification_failed)

266

def handle_otp_failure(sender, **kwargs):

267

"""Log OTP verification failures."""

268

request = kwargs.get('request')

269

user = kwargs.get('user')

270

271

if request and user:

272

logger.warning(

273

f"OTP verification failed for user {user.username} "

274

f"from IP {request.META.get('REMOTE_ADDR')}"

275

)

276

```

277

278

### URLconf Integration

279

280

```python

281

# urls.py

282

from django_otp.views import LoginView

283

from django.urls import path

284

285

urlpatterns = [

286

path('login/', LoginView.as_view(), name='login'),

287

path('logout/', LogoutView.as_view(), name='logout'),

288

# ... other URLs

289

]

290

```

291

292

### Template Usage

293

294

```html

295

<!-- login.html -->

296

<form method="post">

297

{% csrf_token %}

298

299

{{ form.username.label_tag }}

300

{{ form.username }}

301

302

{{ form.password.label_tag }}

303

{{ form.password }}

304

305

{% if form.otp_device %}

306

{{ form.otp_device.label_tag }}

307

{{ form.otp_device }}

308

309

{{ form.otp_token.label_tag }}

310

{{ form.otp_token }}

311

312

{% if form.otp_challenge %}

313

<button type="submit" name="otp_challenge" value="1">

314

Send Challenge

315

</button>

316

{% endif %}

317

{% endif %}

318

319

<button type="submit">Login</button>

320

</form>

321

```

322

323

## Configuration Settings

324

325

```python

326

# settings.py

327

328

# URL for OTP login page (default: /login/)

329

OTP_LOGIN_URL = '/auth/login/'

330

331

# Hide sensitive data in admin (default: False)

332

OTP_ADMIN_HIDE_SENSITIVE_DATA = True

333

```