- verify_oauth2_token now passes GOOGLE_CLIENT_ID as third arg (audience check)
- fail-closed: returns 503 if GOOGLE_CLIENT_ID env var is not set
- add GOOGLE_CLIENT_ID = os.environ.get('GOOGLE_CLIENT_ID', '') to settings
- replace ClerkLoginViewTests with GoogleLoginViewTests (4 cases)
- update requirements-docker.txt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
11 KiB
11 KiB
Changelog
All notable changes to the Eventify Backend are documented here. Format follows Keep a Changelog, versioning follows Semantic Versioning.
[1.10.0] — 2026-04-10
Security
GoogleLoginViewaudience-check fix (POST /api/user/google-login/) — CRITICAL security patchverify_oauth2_token(token, google_requests.Request())was called without the thirdaudienceargument, meaning any valid Google-signed ID token from any OAuth client was accepted — token spoofing from external apps was trivially possible- Fixed to
verify_oauth2_token(token, google_requests.Request(), settings.GOOGLE_CLIENT_ID)— only tokens whoseaudclaim matches our registered Client ID are now accepted - Added fail-closed guard: if
settings.GOOGLE_CLIENT_IDis empty the view returns HTTP 503 instead of silently accepting all tokens
Changed
- Removed Clerk scaffolding — the
@clerk/reactbroker approach added in a prior iteration has been replaced with direct Google Identity Services (GIS) ID-token flow on the frontend. Simpler architecture: one trust boundary instead of three.- Removed
ClerkLoginView,_clerk_jwks_client,_get_clerk_jwks_client()frommobile_api/views/user.py - Removed
path('user/clerk-login/', ...)frommobile_api/urls.py - Removed
CLERK_JWKS_URL/CLERK_ISSUER/CLERK_SECRET_KEYfromeventify/settings.py; replaced withGOOGLE_CLIENT_ID = os.environ.get('GOOGLE_CLIENT_ID', '') - Removed
PyJWT[crypto]>=2.8.0andrequests>=2.31.0fromrequirements.txt+requirements-docker.txt(no longer needed;google-auth>=2.0.0handles verification)
- Removed
Added
- Settings:
GOOGLE_CLIENT_ID = os.environ.get('GOOGLE_CLIENT_ID', '')ineventify/settings.py - Tests:
mobile_api/tests.py::GoogleLoginViewTests— 4 cases: valid token creates user (audience arg verified), missingid_token→ 400,ValueError(wrong sig / wrong aud) → 401, existing user reuses DRF token
Context
- The consumer SPA (
app.eventifyplus.com) now loads the Google Identity Services script dynamically and POSTs a Google ID token to the existing/api/user/google-login/endpoint. Django is the sole session authority.localStorage.event_token/event_userare unchanged. - Deploy requirement: set
GOOGLE_CLIENT_IDin the Django container.envbefore deploying — without it the view returns 503 (fail-closed by design).
[1.9.0] — 2026-04-07
Added
- Lead Manager — new
Leadmodel inadmin_apifor tracking Schedule-a-Call form submissions and sales inquiries- Fields: name, email, phone, event_type, message, status (new/contacted/qualified/converted/closed), source (schedule_call/website/manual), priority (low/medium/high), assigned_to (FK User), notes
- Migration
admin_api/0003_leadwith indexes on status, priority, created_at, email
- Consumer endpoint
POST /api/leads/schedule-call/— public (AllowAny, CSRF-exempt) endpoint for the Schedule a Call modal; creates Lead with status=new, source=schedule_call - Admin API endpoints (all IsAuthenticated):
GET /api/v1/leads/metrics/— total, new today, counts per statusGET /api/v1/leads/— paginated list with filters (status, priority, source, search, date_from, date_to)GET /api/v1/leads/<id>/— single lead detailPATCH /api/v1/leads/<id>/update/— update status, priority, assigned_to, notes
- RBAC:
leadsadded toALL_MODULES,get_allowed_modules(), andStaffProfile.SCOPE_TO_MODULE
[1.8.3] — 2026-04-06
Fixed
TopEventsAPInow works without authentication —POST /api/events/top-events/hadAllowAnypermission but still calledvalidate_token_and_get_user(), returning{"status":"error","message":"token and username required"}for unauthenticated requests- Removed
validate_token_and_get_user()call entirely - Added
event_status='published'filter (wasis_top_event=Trueonly) - Added
event_type_namefield resolution:e.event_type.event_type if e.event_type else ''—model_to_dict()only returns the FK integer
- Removed
[1.8.2] — 2026-04-06
Fixed
FeaturedEventsAPInow returnsevent_type_namestring —model_to_dict()serialises theevent_typeFK as an integer ID; the hero slider frontend readsev.event_type_nameto display the category badge, which was alwaysnull- Added
data_dict['event_type_name'] = e.event_type.event_type if e.event_type else ''aftermodel_to_dict(e)to resolve the FK to its human-readable name (e.g."Festivals") - No frontend changes required —
fetchHeroSlides()already falls back toev.event_type_name
- Added
[1.8.1] — 2026-04-06
Fixed
FeaturedEventsAPInow works without authentication —POST /api/events/featured-events/hadAllowAnypermission but still calledvalidate_token_and_get_user(), causing the endpoint to return HTTP 200 +{"status":"error","message":"token and username required"}for unauthenticated requests (e.g. the desktop hero slider)- Removed the
validate_token_and_get_user()call entirely — the endpoint is public by design and requires no token - Also tightened the queryset to
event_status='published'(wasis_featured=Trueonly) to matchConsumerFeaturedEventsViewbehaviour and avoid returning draft/cancelled events - Root cause: host Nginx routes
/api/→eventify-backendcontainer (port 3001), noteventify-django(port 8085); thevalidate_token_and_get_usergate in this container was silently blocking all hero slider requests
- Removed the
[1.8.0] — 2026-04-04
Added
BulkUserPublicInfoView(POST /api/user/bulk-public-info/)- Internal endpoint for the Node.js gamification server to resolve user details
- Accepts
{ emails: [...] }(max 500), returns{ users: { email: { display_name, district, eventify_id } } } - Used for leaderboard data bridge (syncing user names/districts into gamification DB)
- CSRF-exempt, returns only public-safe fields (no passwords, tokens, or sensitive PII)
[1.7.0] — 2026-04-04
Added
- Home District with 6-month cooldown
district_changed_atDateTimeField on User model (migration0013_user_district_changed_at) — nullable, no backfill; NULL means "eligible to change immediately"VALID_DISTRICTSconstant (14 Kerala districts) inaccounts/models.pyfor server-side validationWebRegisterFormnow accepts optionaldistrictfield; stampsdistrict_changed_aton valid selection during signupUpdateProfileViewenforces 183-day (~6 months) cooldown — rejects district changes within the window with a human-readable "Next change: {date}" errordistrict_changed_atincluded in all relevant API responses:LoginView,WebRegisterView,StatusView,UpdateProfileViewStatusViewnow also returnsdistrictfield (was previously missing)
[1.6.2] — 2026-04-03
Security
- Internal exceptions no longer exposed to API callers — all 15
except Exception as eblocks acrossmobile_api/views/user.pyandmobile_api/views/events.pynow log the real error viaeventify_loggerand return a generic"An unexpected server error occurred."to the caller- Affected views:
RegisterView,WebRegisterView,LoginView,StatusView,LogoutView,UpdateProfileView,EventTypeAPI,EventListAPI,EventDetailAPI,EventImagesListAPI,EventsByDateAPI,DateSheetAPI,PincodeEventsAPI,FeaturedEventsAPI,TopEventsAPI StatusViewandUpdateProfileViewwere also missinglog(...)calls entirely — addedfrom eventify_logger.services import logimport added toevents.py(was absent)
- Affected views:
[1.6.1] — 2026-04-03
Added
eventify_idinStatusViewresponse (/api/user/status/) — consumer app uses this to refresh the Eventify ID badge (EVT-XXXXXXXX) for sessions that pre-date theeventify_idlogin fieldaccountsmigration0012_user_eventify_iddeployed to production containers — backfilled all existing users with unique Eventify IDs; previously the migration existed locally but had not been applied in production
[1.6.0] — 2026-04-02
Added
- Unique Eventify ID system (
EVT-XXXXXXXXformat)- New
eventify_idfield onUsermodel —CharField(max_length=12, unique=True, editable=False, db_index=True) - Charset
ABCDEFGHJKLMNPQRSTUVWXYZ23456789(no ambiguous characters I/O/0/1) giving ~1.78T combinations - Auto-generated on first
save()via a 10-attempt retry loop usingsecrets.choice() - Migration
0012_user_eventify_id: add nullable → backfill all existing users → make non-null
- New
eventify_idexposed inaccounts/api.py→_partner_user_to_dict()fields listeventify_idexposed inpartner/api.py→_user_to_dict()fields listeventify_idexposed inmobile_api/views/user.py→LoginViewresponse (populateslocalStorage.event_user.eventify_id)eventifyIdexposed inadmin_api/views.py→_serialize_user()(camelCase for direct TypeScript compatibility)- Server-side search in
UserListViewnow also filters oneventify_id__icontains - Synced migration
0011_user_allowed_modules_alter_user_id(pulled from server, was missing from local repo)
Changed
accounts/models.py: mergedallowed_modulesfield +get_allowed_modules()+ALL_MODULESconstant from server (previously only existed on server)
[1.5.0] — 2026-03-31
Added
allowed_modulesTextField onUsermodel — comma-separated module slug access controlget_allowed_modules()method onUser— returns list of accessible modules based on role or explicit listALL_MODULESclass constant listing all platform module slugs- Migration
0011_user_allowed_modules_alter_user_id
[1.4.0] — 2026-03-24
Added
- Partner portal login/logout APIs (
accounts/api.py) —PartnerLoginAPI,PartnerLogoutAPI,PartnerMeAPI _partner_user_to_dict()serializer for partner-scoped user data- Partner CRUD, KYC review, and user management endpoints in
partner/api.py
[1.3.0] — 2026-03-14
Changed
- User
idfield changed fromAutoFieldtoBigAutoField(migration0010_alter_user_id)
[1.2.0] — 2026-03-10
Added
partnerForeignKey onUsermodel linking users to partners (migration0009_user_partner)- Profile picture upload support (
ImageField) withdefault.pngfallback (migration0006–0007)
[1.1.0] — 2026-02-28
Added
- Location fields on
User:pincode,district,state,country,place,latitude,longitude - Custom
UserManagerfor programmatic user creation
[1.0.0] — 2026-03-01
Added
- Initial Django project with custom
Usermodel extendingAbstractUser - Role choices:
admin,manager,staff,customer,partner,partner_manager,partner_staff,partner_customer - JWT authentication via
djangorestframework-simplejwt - Admin API foundation: auth, dashboard metrics, partners, users, events
- Docker + Gunicorn + PostgreSQL 16 production setup