Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions codepress_documentation/.last-documented-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5b1e774beba7d63b73b5a3f3c1e16afa54706363
8 changes: 8 additions & 0 deletions codepress_documentation/INDEX.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Codepress Documentation Index

| Feature | Slug | Area | Last Updated | Summary |
|---|---|---|---|---|
| Multifactor Auth Core | `multifactor-auth-core` | `deux/` | 2026-06-15 | Core MFA model, settings, TOTP service, and REST API for enabling/disabling/verifying MFA and managing backup codes. |
| SMS Challenge | `sms-challenge` | `deux/` | 2026-06-15 | Two-step SMS enrollment flow using Twilio and TOTP; the primary MFA delivery mechanism in Deux. |
| Token Auth MFA | `token-auth-mfa` | `deux/authtoken/` | 2026-06-15 | DRF token authentication extended with an MFA gate; supports mfa_code and backup_code on the token endpoint. |
| OAuth2 MFA | `oauth2-mfa` | `deux/oauth2/` | 2026-06-15 | django-oauth-toolkit integration that adds MFA verification to the OAuth2 password grant token endpoint. |
40 changes: 40 additions & 0 deletions codepress_documentation/features/multifactor-auth-core/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
feature: multifactor-auth-core
area: deux/
created: 2026-06-15
last_updated: 2026-06-15
---

# Multifactor Auth Core

## Overview

Deux is a Django REST Framework package that adds multifactor authentication (MFA) to existing Django applications. It provides an abstract model layer for storing per-user MFA state, a TOTP-based code generation and verification service, and a set of REST API endpoints for enabling, verifying, disabling, and recovering MFA.

## Architecture

The core is built around `AbstractMultiFactorAuth`, an abstract Django model that applications extend to attach MFA state to their user records. The library ships a concrete `MultiFactorAuth` subclass for projects that do not need customization. Application settings are read from a `DEUX` dict in Django's `settings.py` and exposed through a lazy `MFASettings` accessor that supports import-string resolution for pluggable components.

The REST API is exposed through Django Rest Framework generic views and serializers. Each challenge type follows a two-step flow: a request endpoint triggers the challenge delivery, and a verify endpoint confirms the code and enables MFA. A separate backup code endpoint allows authenticated users to retrieve a one-time recovery code that disables MFA when used.

## Key Files

- `deux/abstract_models.py` - Abstract `AbstractMultiFactorAuth` model with enable/disable/backup-code logic
- `deux/models.py` - Concrete `MultiFactorAuth` model (blank subclass of the abstract model)
- `deux/app_settings.py` - `MFASettings` class; reads from `settings.DEUX`, supports import-string resolution
- `deux/services.py` - TOTP code generation (`generate_mfa_code`), verification (`verify_mfa_code`), and `MultiFactorChallenge` dispatcher
- `deux/serializers.py` - DRF serializers for MFA detail, challenge request/verify, and backup codes
- `deux/views.py` - DRF generic views for MFA detail, SMS challenge request/verify, and backup codes
- `deux/urls.py` - URL patterns wired to the core views
- `deux/constants.py` - Challenge type constants (`SMS`, `DISABLED`)
- `deux/validators.py` - Phone number validator
- `deux/exceptions.py` - Custom exception types
- `deux/strings.py` - User-facing error and message strings

## Detailed Documentation

- [Settings Reference](./settings-reference.md) - All configurable `DEUX` settings and their defaults

## Keywords

MFA, multifactor authentication, two-factor authentication, 2FA, TOTP, OTP, Django, DRF, Django Rest Framework, backup code, challenge, SMS, abstract model, MultiFactorAuth, enable MFA, disable MFA, recovery code
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Settings Reference

> Part of [Multifactor Auth Core](./README.md)

All settings are placed under the `DEUX` key in Django's `settings.py`.

## Available Settings

| Setting | Default | Description |
|---|---|---|
| `BACKUP_CODE_DIGITS` | `12` | Number of characters in a backup code (taken from the start of the hex backup key, uppercased) |
| `MFA_CODE_NUM_DIGITS` | `6` | Number of digits in a TOTP code |
| `MFA_MODEL` | `"deux.models.MultiFactorAuth"` | Import path of the MFA model class; supports import-string notation |
| `SEND_MFA_TEXT_FUNC` | `"deux.notifications.send_mfa_code_text_message"` | Import path of the function used to send the MFA code via SMS |
| `STEP_SIZE` | `30` | TOTP time step in seconds |
| `TWILIO_ACCOUNT_SID` | `""` | Twilio account SID for SMS delivery |
| `TWILIO_AUTH_TOKEN` | `""` | Twilio auth token |
| `TWILIO_SMS_POOL_SID` | `""` | Twilio messaging service SID (from phone number) |

## Customization Points

- **Custom MFA model**: set `MFA_MODEL` to a subclass of `AbstractMultiFactorAuth` to add extra fields or behavior.
- **Custom SMS sender**: set `SEND_MFA_TEXT_FUNC` to any callable that accepts `(mfa_instance, mfa_code)` to replace the Twilio default with another SMS provider or a no-op for testing.
34 changes: 34 additions & 0 deletions codepress_documentation/features/oauth2-mfa/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
feature: oauth2-mfa
area: deux/oauth2/
created: 2026-06-15
last_updated: 2026-06-15
---

# OAuth2 MFA

## Overview

Deux integrates with `django-oauth-toolkit` to add an MFA gate to the OAuth2 password grant flow. When a user with MFA enabled requests a token using their username and password, the system triggers the appropriate challenge before issuing the token. The client must re-submit with the MFA code or backup code to complete the OAuth2 token exchange.

## Architecture

The integration is composed of two pieces that override the django-oauth-toolkit defaults:

`MFATokenView` extends `TokenView` and wires in the custom backend and validator via class attributes. `MFARequestBackend` extends `OAuthLibCore` to extract `mfa_code` and `backup_code` from the request body and pass them as extra credentials to the OAuthLib server. It also handles DRF request coercion so the body can be parsed correctly even when the request comes in through a DRF view.

`MFAOAuth2Validator` extends `OAuth2Validator` and overrides `validate_user` to implement the MFA check. The validation logic mirrors the token auth flow: trigger a challenge when credentials are correct but no code is provided, verify the TOTP code or consume the backup code when one is supplied, and reject the request on any mismatch.

URL-encoded request bodies are supported in addition to the standard OAuth2 form-encoded format, handled in `MFARequestBackend.extract_body`.

## Key Files

- `deux/oauth2/views.py` - `MFATokenView` — wires backend and validator
- `deux/oauth2/backends.py` - `MFARequestBackend` — extracts MFA credentials from request body
- `deux/oauth2/validators.py` - `MFAOAuth2Validator` — validates user with MFA gate
- `deux/oauth2/exceptions.py` - `ChallengeRequiredMessage` / `InvalidLoginError`
- `deux/oauth2/urls.py` - URL patterns for the OAuth2 token endpoint

## Keywords

OAuth2, django-oauth-toolkit, password grant, token endpoint, MFATokenView, MFARequestBackend, MFAOAuth2Validator, mfa_code, backup_code, OAuthLib, url-encoded, extra credentials
33 changes: 33 additions & 0 deletions codepress_documentation/features/sms-challenge/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
feature: sms-challenge
area: deux/
created: 2026-06-15
last_updated: 2026-06-15
---

# SMS Challenge

## Overview

The SMS challenge is the primary MFA delivery mechanism in Deux. When a user initiates MFA enrollment, the system generates a TOTP code and sends it to the user's phone via Twilio. The user then submits the code back to verify ownership of the phone number, which activates MFA on their account.

## Architecture

The two-step SMS enrollment flow is handled by a pair of DRF endpoints. The request step accepts a phone number, triggers code generation via the `MultiFactorChallenge` service, and sends the code through the configured SMS function. The verify step checks the submitted code using TOTP verification with a ±1 time-step drift window to account for clock skew, then enables MFA on success.

The default SMS delivery function uses the Twilio REST API. It can be swapped out entirely via the `SEND_MFA_TEXT_FUNC` setting, making it straightforward to use other SMS providers or a stub during testing.

Codes are generated using the `django-otp` TOTP implementation with the per-user `sms_secret_key` stored on the MFA model. Verification checks the current time step plus one step before and after to handle slight time differences.

## Key Files

- `deux/services.py` - `generate_mfa_code` / `verify_mfa_code` — TOTP generation and verification; `MultiFactorChallenge` — dispatches to the SMS challenge method
- `deux/notifications.py` - `send_mfa_code_text_message` — default Twilio SMS delivery function
- `deux/serializers.py` - `SMSChallengeRequestSerializer` / `SMSChallengeVerifySerializer`
- `deux/views.py` - `SMSChallengeRequestDetail` / `SMSChallengeVerifyDetail`
- `deux/urls.py` - `/sms/request/` and `/sms/verify/` URL patterns
- `deux/exceptions.py` - `InvalidPhoneNumberError` / `TwilioMessageError` / `FailedChallengeError`

## Keywords

SMS, Twilio, TOTP, OTP, one-time password, phone number, MFA enrollment, challenge request, challenge verify, time-based, drift, text message, two-factor
33 changes: 33 additions & 0 deletions codepress_documentation/features/token-auth-mfa/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
feature: token-auth-mfa
area: deux/authtoken/
created: 2026-06-15
last_updated: 2026-06-15
---

# Token Auth MFA

## Overview

Deux extends Django REST Framework's built-in token authentication to add an MFA gate. When a user with MFA enabled submits their username and password, the system triggers the appropriate challenge instead of immediately returning a token. The client must then re-submit with the MFA code (or a backup code) to receive the authentication token.

## Architecture

`ObtainMFAAuthToken` extends DRF's `ObtainAuthToken` view, swapping in `MFAAuthTokenSerializer` as its serializer. The serializer extends DRF's `AuthTokenSerializer` to accept two optional extra fields: `mfa_code` and `backup_code`.

The validation logic follows a clear priority:
1. If the user has no MFA or MFA is disabled — authenticate normally and return the token.
2. If MFA is enabled and neither code is supplied — trigger the challenge and return `{mfa_required: true, mfa_type: "<type>"}` with HTTP 200.
3. If `mfa_code` is supplied — verify it against the user's TOTP key; reject if invalid.
4. If `backup_code` is supplied — check and consume it, disabling MFA as a side effect; reject if invalid.
5. Supplying both codes simultaneously is rejected.

## Key Files

- `deux/authtoken/views.py` - `ObtainMFAAuthToken` view
- `deux/authtoken/serializers.py` - `MFAAuthTokenSerializer`
- `deux/authtoken/urls.py` - URL patterns for the token auth endpoint

## Keywords

token authentication, DRF token auth, ObtainAuthToken, MFA token, auth token, login, credential, mfa_code, backup_code, token endpoint