0.1.111 — Restore email/username + password login (Casdoor-down fallback)
User directive verbatim:
"you may need to restore that. Our goal is to keep app running for existing users even when casdoor is down. (keep simple login, disable register, reset passwords, email verification functions.)"
0.1.106 deleted LoginApiView along with the rest of the legacy
auth pipeline (register, password reset, email verification,
sessions). After Casdoor came online, the assumption was that
SSO would always be reachable. The user has now asked to restore
only the login endpoint so existing accounts retain a
fallback when auth.trance-0.com is unreachable. Register /
password reset / email verification stay deleted per the
directive — those flows continue to live entirely in Casdoor.
What's restored
POST /api/v1/auth/login/ — the same wire shape the SPA's
client.login() already posts ({email, password},
historically with identifier / username aliases).
The new view (backend/creators/api.py,
LoginApiView + LoginSerializer) does the minimum:
- Looks up the supplied email or username, case-insensitively.
- Calls
django.contrib.auth.authenticate()against the stored password hash. No code-path bypasses the hasher. - On success, calls
Token.objects.get_or_create(user=user)(DRF's stockauthtoken_tokentable — already inINSTALLED_APPSsince long before the Casdoor migration). - Returns
auth_payload(user, token=token.key, request)— same shape as the Casdoor exchange flow, so the SPA's existing token-handling code keeps working unchanged. The SPA resends the token asAuthorization: Token <hex>for every subsequent call, whichrest_framework.authentication.TokenAuthenticationvalidates (still inDEFAULT_AUTHENTICATION_CLASSESpernotechondria/settings.py:303).
Token.objects.get_or_create is idempotent — repeated logins
return the same token row. If you want forced rotation, the
existing /auth/rotate-api-key/ route covers it.
What's not restored (deliberately)
Per the user directive:
- Register — no
RegisterApiViewis added back. - Password reset — no
PasswordResetRequestApiView/PasswordResetConfirmApiView. Use Casdoor's user portal. - Email verification — no
VerifyEmailApiView/ResendVerificationApiView/ SMTP code path. TheVerificationCodemodel +_send_code_emailhelper stay deleted. - Per-device session list — no
SessionApiView/SessionListApiView/SessionRevokeApiView. Thecreators.Sessionmodel stays deleted; its replacement is the user's Casdoor portal session list. /auth/logout/— not added back; client-side logout drops the locally-stored token, and the server-side token row stays valid until the user explicitly rotates the key (sufficient for the fallback case).
Files changed
backend/creators/api.py- New imports:
from django.contrib.auth import authenticate,from rest_framework.authtoken.models import Token. - New
LoginSerializerandLoginApiViewclasses immediately beforeSettingsSerializer. Section banner comment notes the restoration scope.
- New imports:
backend/notechondria/api_urls.py- Added
LoginApiViewto thecreators.apiimport. - Added
path("auth/login/", LoginApiView.as_view(), name="auth-login")immediately after the rotate-api-key route, with a comment block documenting the fallback role.
- Added
No model changes, no migrations.
Operator runbook
- Casdoor stays primary. The editor's signed-out account
card surfaces Casdoor SSO as the default CTA (per 0.1.108).
Tapping the legacy "Email + password" expander still calls
the same
client.login()it always has — which now reaches a real endpoint instead of 404'ing. - Existing accounts keep their stored Django password.
Because the legacy 0.1.105 auth pipeline used Django's
standard hasher, the password hashes in
auth_user.passwordare still valid. No password reset run is needed; users sign in with whatever credential they last set. - New users sign up via Casdoor. There is no in-app
registration. Direct them to the Casdoor signup link
(
<endpoint>/signup/<orgName>— the org-themed signup page, exposed by the SPA via the existingcasdoorOrgLoginUrlpattern). - Forgotten password — the user changes it in their
Casdoor account portal, then either signs in via SSO or
asks an operator to reset their Django password from the
Django admin (
/admin/auth/user/) for the fallback path.
Verification
python3 -m py_compileclean onbackend/creators/api.pyandbackend/notechondria/api_urls.py.rest_framework.authtokenalready inINSTALLED_APPS(notechondria/settings.py:100) and DRF stockTokenAuthenticationalready inDEFAULT_AUTHENTICATION_CLASSES(settings.py:303), so the existingauthtoken_tokentable is reused — no new migration needed.- Behavior unchanged for any caller that doesn't post to
/auth/login/. The route is purely additive.
Note on the production deploy
The last user-supplied log shows the production backend is
still 404'ing on /api/v1/auth/casdoor/config/, which means
the deployed image predates 0.1.96 entirely. Until that
backend is redeployed:
- Casdoor SSO won't work (route missing).
- This round's
/auth/login/won't work either (route also missing).
After redeploy, both work. The 0.1.110 group-ACL gate
(CASDOOR_REQUIRED_GROUPS=trance-0/app-notechondria) only
applies to JWT auth — the legacy /auth/login/ path is not
gated by Casdoor groups, so the fallback works for any active
Django user regardless of their Casdoor group membership. If
you want to keep the legacy login locked down to a smaller
set, restrict auth_user.is_active directly via the Django
admin.