0.1.106 — drop invitation codes, email-verification codes, sessions, change-password / change-email; SMTP code path retired

The Casdoor cutover is functionally complete. With Casdoor handling signup, password reset, and session management at auth.trance-0.com, the Notechondria-side equivalents became dead weight. This round deletes them.

User's directive verbatim: "since we move the full auth cycle process to casdoor, there is no need for invitation codes and auth codes, remove those and try to integrate session management with casdoor integration, if it is too complicated or not supported by casdoor offical api, just remove the function."

Decision: sessions removed, not integrated

Casdoor's admin SDK exposes get_sessions(user_owner) but its response shape is incompatible with the existing Notechondria Session model + per-app Active Sessions card. Integrating would mean rewriting both ends — and Casdoor's user portal already lets users view + revoke their own sessions. Per the user's "just remove if complicated" hint, the whole Notechondria-side session surface is gone. Sessions live on Casdoor's side now.

Backend — what's deleted

backend/creators/models.py

  • InvitationCode — class + every field. Was the gate for legacy email-based signup.
  • VerificationCode + VerificationChoices — the 6-digit-code records that backed email verification, password reset, and identity confirmation.
  • Session + the SESSION_IDLE_TIMEOUT / SESSION_ABSOLUTE_TIMEOUT constants — the per-device session table that MultiSessionAuthentication used to validate DRF tokens against.

backend/creators/migrations/0031_drop_invitation_verification_session.py (new)

DeleteModel("InvitationCode"), DeleteModel("VerificationCode"), DeleteModel("Session"), depending on 0030_creator_casdoor_sub. Existing migration history is preserved — Django needs the original CreateModel migrations to apply on a fresh DB before the new DeleteModel rolls them back, so production deploys with an existing DB run all 31 migrations and end up with the same final schema.

backend/creators/api.py

Deleted endpoints (and their serializers, where serializer classes were used only by the deleted view):

  • RegisterApiView, ValidateInvitationApiView, VerifyEmailApiView, ResendVerificationApiView
  • LoginApiView — Casdoor handles login now via the CasdoorJWTAuthentication class + the casdoor/exchange/ endpoint. The frontend's legacy client.login() method will now 404 if any code path still calls it.
  • LogoutApiView — only existed to revoke a Session row; with no Session model, the endpoint is meaningless. Frontend logout state-clears client-side regardless.
  • PasswordResetRequestApiView, PasswordResetConfirmApiView, SendIdentityCodeApiView, ChangePasswordApiView, ChangeEmailApiView — all four backed by VerificationCode + the _send_code_email helper, all dead now.
  • SessionApiView, SessionListApiView, SessionRevokeApiView.

backend/creators/authentication.py

MultiSessionAuthentication deleted (it only existed to validate Session model rows). DRF stock TokenAuthentication plus ApiKeyAuthentication plus CasdoorJWTAuthentication cover every remaining auth path.

backend/creators/utils.py

Email-send helpers deleted: _send_code_email, send_registration_email, send_password_reset_email, issue_registration_code, issue_password_reset_code, smtp_is_configured, log_manual_verification_code. The from django.core.mail import send_mail import comes out too; nothing in creators/ imports it now.

Avatar / ensure_creator / etc. all preserved.

backend/notechondria/api_urls.py

14 dead URL routes pruned. Only auth/rotate-api-key/ and the four auth/casdoor/* routes survive on the auth surface.

backend/notechondria/settings.py

  • SMTP env-var loads gone (SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, SMTP_USE_TLS, SMTP_USE_SSL, SMTP_FROM_EMAIL, SMTP_EMAIL_VERIFICATION_TTL_HOURS) and the EMAIL_* Django settings derived from them.
  • FRONTEND_VERIFY_URL env-var load gone — was the verification- email link target.
  • MultiSessionAuthentication removed from DEFAULT_AUTHENTICATION_CLASSES. New order: ApiKeyAuthenticationCasdoorJWTAuthentication → DRF stock TokenAuthentication.

backend/creators/forms.py, backend/creators/views.py, backend/creators/urls.py, backend/creators/admin.py

RegisterForm, validate_registration_code, register_request view, creators:register URL, and InvitationCodeAdmin / VerificationCodeAdmin / ActivationCodeInline — all the dependent classic-Django (non-DRF) plumbing — deleted. The creators:register link in notechondria/templates/navbar.html removed too so template rendering doesn't NoReverseMatch.

backend/creators/tests.py

Test cases that exercised the removed surfaces deleted in full: AuthApiTests login coverage, RegistrationFlowTests, InvitationCodeTests, PasswordResetFlowTests, VerificationCodeModelTests, OAuthBindRejectionTests. The remaining fixtures (GithubSyncTests, CasdoorAuthTests) had their Session.create_for_user calls swapped for DRF Token.objects.create since the in-test session was just a way to make a request authenticated.

Frontend — what's deleted

frontend/notechondria_shared/

  • lib/src/components/auth_dialogs_wizard.dart — file deleted. Held only RegistrationWizard; no remaining callers since 0.1.103.
  • lib/src/components/active_sessions_card.dart — file deleted. Held only ActiveSessionsCard + _ActiveSessionRow; no remaining callers post-Active-Sessions removal.
  • lib/src/components/auth_dialogs.dartEmailCodeDialog and PasswordResetDialog classes + their states deleted. Kept EmailPasswordDialog (still backs the legacy email/password Login fallback expander), FeedbackText, AuthHub.
  • lib/notechondria_shared.dart — exports trimmed to drop EmailCodeDialog, PasswordResetDialog, RegistrationWizard, ActiveSessionsCard.
  • lib/src/app_shell/auth_client.dart — abstract methods reduced to login, getCasdoorConfig, casdoorExchange, casdoorBind, casdoorUnlink, checkSession, logout, getSettings, updateSettings. Dropped register, verifyEmail, resendVerification, requestPasswordReset, confirmPasswordReset, listSessions, revokeSession.
  • lib/src/app_shell/app_shell_auth_actions_mixin.dart — kept login only. Dropped the five register / verify / reset helpers.

Per-app cascade (editor / planner / portal)

  • lib/core/client.dart (+ client_base.dart / http_client.dart) — abstract method declarations + concrete implementations dropped: register, validateInvitation, verifyEmail, resendVerification, requestPasswordReset, confirmPasswordReset, sendIdentityCode, listSessions, revokeSession, changePassword, changeEmailRequest, changeEmailConfirm. login and checkSession retained (the latter will 404 against the new backend, which the boot- restore flow already treats as "anonymous").
  • lib/app_shell.dart_SettingsPage(...) named-arg passthroughs trimmed to match. Dropped _currentSessionId, _multiDevice, _otherSessionsCount fields. applySessionMetadata / clearSessionMetadata collapsed to the mixin's no-op default.
  • lib/modules/settings.dart_SettingsPage constructor + field declarations stripped of every dropped callback.
  • lib/modules/settings_pages.dart (editor, portal) — _SignInSecurityPage shrank to the Casdoor bind/unlink card + related plumbing; Active Sessions row + Change Email / Change Password rows gone. Multi-device banner gone.
  • frontend/editor_app/lib/modules/settings_build.dart — dropped _openChangePasswordDialog and _openChangeEmailDialog extension methods.
  • frontend/portal_app/lib/modules/settings_dialogs.dart — file deleted (held the _openChangePasswordDialog / _openChangeEmailDialog extension that wrapped the dropped endpoints).
  • frontend/portal_app/lib/main.dart — dropped the part 'modules/settings_dialogs.dart' directive.

migrate_users_to_casdoor flag trim

The --invitation-code flag added in 0.1.105 came back out — with invitation codes deleted, the flag had no semantic anchor. --admin-password-from-env stays.

Verification

  • python -m py_compile on every modified backend file → exit 0.
  • DJANGO_SECRET_KEY=test python manage.py checkSystem check identified no issues (0 silenced).
  • DJANGO_SECRET_KEY=test python manage.py makemigrations --dry-run → reports only the same pre-existing notes/0019_alter_noteattachment_id.py that has been pending since 0.1.101. No new pending migrations introduced.
  • flutter analyze from frontend/notechondria_shared/, frontend/editor_app/, frontend/planner_app/, frontend/portal_app/ → 0 errors each.

Operator runbook (after 0.1.106 deploys)

  1. git push — deploy 0.1.106 to Northflank. The build picks up the new _drop_invitation_verification_session migration on first boot and drops the three tables. Existing data in those tables is wiped — confirm before deploying that you don't want any of it (you almost certainly don't, since it's all dead auth state).
  2. Confirm https://notechondria.trance-0.com/api/v1/handshake/ reports version: "0.1.106" and a real commit / build_time.
  3. Confirm /api/v1/auth/casdoor/config/ returns {configured: true, ...} (assuming the backend has the CASDOOR_* env vars — the frontend now logs a debug-log warning if it doesn't).
  4. From the Northflank shell, run the user migration if not already done — see docs/versions/0.1.105.md for the runbook.
  5. After deploy, log in once via Casdoor SSO. Check that:
    • Sign-up button is gone from the auth surface.
    • "Forgot password" is gone from the legacy login dialog.
    • "Active Sessions" card is gone from the Settings page.
    • "Change password" / "Change email" rows are gone from _SignInSecurityPage.

Carryover

  • client.login() and client.logout() Dart methods stay in the per-app client classes for now. They'll 404 against the new backend; the legacy EmailPasswordDialog Login fallback in AuthHub still calls login(). Since Casdoor SSO is the primary path and the fallback only matters for un-migrated accounts, a follow-up round can drop EmailPasswordDialog + the matching login() plumbing once you've confirmed every active user is in Casdoor.
  • docker-compose.yml still references the dropped SMTP_* and FRONTEND_VERIFY_URL env vars. Harmless — Django no longer reads them.
  • entrypoint.sh's legacy 0.1.65 wipe block does from creators.models import Session inside a try/except; after the migration runs, the import fails and the except branch logs "Skipped session wipe: ...". Functionally fine; cleanup deferred.