3 Commits

Author SHA1 Message Date
mohd 0c992404ea chore: removed unused otp providers 2026-03-13 16:25:26 +03:00
mohd d796d9e6a1 removed unviable e2e test 2026-03-13 16:21:25 +03:00
mohd 2ba0cfffc8 chore: adjust near-term focus 2026-03-13 16:11:30 +03:00
4 changed files with 2 additions and 147 deletions
+2 -22
View File
@@ -7,25 +7,5 @@
- Phone-first auth works, but `USERNAME_FIELD` is email; align identifier strategy to avoid future auth confusion.
## Near-Term Focus
- Hardening Authentica integration (timeouts, retries, async delivery) and aligning notification provider choices.
**Authentica E2E**
Run the real Authentica OTP flow only when explicitly enabled.
Env vars (in `backend/.env` or shell):
- `AUTHENTICA_E2E=1`
- `AUTHENTICA_API_KEY=...`
- `AUTHENTICA_E2E_PHONE=...` (must receive OTP)
- `AUTHENTICA_E2E_CODE=...` (required; no interactive prompt)
Command:
```bash
cd backend
PYTEST_ADDOPTS='' python3 -m pytest apps/accounts/tests -m external
```
Suggested flow:
1. Trigger the E2E test to send the OTP, then set `AUTHENTICA_E2E_CODE` and re-run if needed.
- Decide and document payment lifecycle scope (capture/refund supported vs explicitly out of scope).
- Add timeouts/logging for external calls or introduce minimal async jobs for OTP/notifications.
- Keep booking, payment, and notification orchestration in service layers, not views.
- finalize otp testing
- work on authentication and complete it
-53
View File
@@ -57,57 +57,6 @@ class ConsoleOtpProvider(BaseOtpProvider):
logger.info("OTP WhatsApp to %s: %s", to_number, message)
class TwilioOtpProvider(BaseOtpProvider):
"""Twilio provider for SMS and WhatsApp OTP delivery. Requires TWILIO_* env vars."""
def __init__(self) -> None:
self.account_sid = os.getenv("TWILIO_ACCOUNT_SID")
self.auth_token = os.getenv("TWILIO_AUTH_TOKEN")
self.from_number = os.getenv("TWILIO_FROM_NUMBER")
self.whatsapp_from = os.getenv("TWILIO_WHATSAPP_FROM")
def _assert_config(self) -> None:
if not self.account_sid or not self.auth_token or not self.from_number:
raise ValueError(_("Twilio credentials are not configured"))
def _get_client(self):
from twilio.rest import Client
self._assert_config()
return Client(self.account_sid, self.auth_token)
def send_sms(self, to_number: str, message: str) -> None:
client = self._get_client()
client.messages.create(body=message, from_=self.from_number, to=to_number)
def send_whatsapp(self, to_number: str, message: str) -> None:
self._assert_config()
if not self.whatsapp_from:
raise ValueError(_("Twilio WhatsApp sender is not configured"))
client = self._get_client()
from_ = f"whatsapp:{self.whatsapp_from}"
to = f"whatsapp:{to_number}"
client.messages.create(body=message, from_=from_, to=to)
class UnifonicOtpProvider(BaseOtpProvider):
def __init__(self) -> None:
self.app_sid = os.getenv("UNIFONIC_APP_SID")
self.sender_id = os.getenv("UNIFONIC_SENDER_ID")
self.whatsapp_sender = os.getenv("UNIFONIC_WHATSAPP_SENDER")
def _assert_config(self) -> None:
if not self.app_sid or not self.sender_id:
raise ValueError(_("Unifonic credentials are not configured"))
def send_sms(self, to_number: str, message: str) -> None:
self._assert_config()
raise NotImplementedError(_("Unifonic SMS adapter not implemented yet"))
def send_whatsapp(self, to_number: str, message: str) -> None:
self._assert_config()
if not self.whatsapp_sender:
raise ValueError(_("Unifonic WhatsApp sender is not configured"))
raise NotImplementedError(_("Unifonic WhatsApp adapter not implemented yet"))
class AuthenticaOtpProvider(BaseOtpProvider):
@@ -197,8 +146,6 @@ class AuthenticaOtpProvider(BaseOtpProvider):
PROVIDERS = {
"console": ConsoleOtpProvider,
"twilio": TwilioOtpProvider,
"unifonic": UnifonicOtpProvider,
"authentica": AuthenticaOtpProvider,
}
@@ -1,72 +0,0 @@
"""Mocked end-to-end phone auth flow using Authentica OTP provider."""
import os
from unittest.mock import MagicMock, patch
import pytest
from django.test import override_settings
from django.urls import reverse
from apps.accounts.models import User
@pytest.mark.django_db
@override_settings(OTP_PROVIDER="authentica")
@patch("requests.post")
def test_phone_auth_flow_with_authentica_mock(mock_post, client):
def make_response(payload, ok=True):
response = MagicMock()
response.ok = ok
response.json.return_value = payload
response.text = ""
return response
def side_effect(url, headers=None, json=None, timeout=None):
assert headers and headers.get("X-Authorization") == "api-key"
assert timeout == 7.0
if url.endswith("/api/v2/send-otp"):
assert json == {"method": "sms", "phone": "+966512345678"}
return make_response({"success": True})
if url.endswith("/api/v2/verify-otp"):
if json == {"phone": "+966512345678", "otp": "123456"}:
return make_response({"verified": True})
return make_response({"verified": False})
raise AssertionError(f"Unexpected URL {url}")
with patch.dict(
os.environ,
{
"AUTHENTICA_API_KEY": "api-key",
"AUTHENTICA_TIMEOUT_SECONDS": "7",
},
):
mock_post.side_effect = side_effect
request_url = reverse("phone_auth_request")
verify_url = reverse("phone_auth_verify")
response = client.post(
request_url,
{"phone_number": "0512345678", "channel": "sms", "first_name": "Sara"},
content_type="application/json",
)
assert response.status_code == 201
request_id = response.json()["request_id"]
bad = client.post(
verify_url,
{"request_id": request_id, "code": "000000"},
content_type="application/json",
)
assert bad.status_code == 400
good = client.post(
verify_url,
{"request_id": request_id, "code": "123456"},
content_type="application/json",
)
assert good.status_code == 200
user = User.objects.filter(phone_number="+966512345678").first()
assert user is not None
assert user.is_phone_verified is True
View File