0.1.103 — in-app signup gone; "Login via third party" + Casdoor signup link

The signed-out auth surface across all three apps is now: Casdoor SSO pill (existing OAuth code flow) → outlined "Login via third party" button (browser redirect to the Casdoor org-login page) → "No account? Sign up via Casdoor" text link (same redirect) → email / password Login behind the existing fallback expander (no signup, no "Forgot password" — admins handle resets).

Operators must:

  1. Have CASDOOR_* env vars populated so /api/v1/auth/casdoor/config/ returns configured: true plus endpoint and organization.
  2. In the Casdoor admin UI for the notechondria application, allow self-registration on the org login page (Organization → Settings → "Account items" → enable signup).
  3. Ensure https://auth.trance-0.com/login/notechondria is reachable.

If signup is disabled at the Casdoor side, the "Sign up via Casdoor" link still works as a destination but the user lands on a login-only page. That's a Casdoor-config decision, not a Notechondria bug.

Shared library — AuthHub rewritten

frontend/notechondria_shared/lib/src/components/auth_dialogs.dart trimmed AuthHub from a six-callback "everything" widget to a four- field hub: onLogin, onCasdoorLogin, casdoorOrgLoginUrl, apiBaseUrl. The body now renders, in priority order:

  1. Continue with Casdoor SSO — the existing OAuth code flow via AppShellOAuthMixin.launchOAuth. Auto-redirects back. Rendered only when onCasdoorLogin != null (backend is in shadow mode otherwise).
  2. Login via third party — direct browser redirect (via the shared url_strategy.browserRedirect helper) to casdoorOrgLoginUrl. Rendered only when that URL is non-empty. Use this when the user wants Casdoor's hosted login page (Google / GitHub / etc., configured on the Casdoor application's Providers tab).
  3. No account? Sign up via Casdoor — text link below the third- party button, same destination. The in-app RegistrationWizard is gone.
  4. Use email / password instead — the existing expander; now contains only a Login button + a small caption telling un-migrated users to contact the administrator for password resets.

EmailPasswordDialog's onForgotPassword parameter was dropped — the in-app password-reset flow is gone.

The shared RegistrationWizard, EmailCodeDialog, and PasswordResetDialog widgets remain in the package as dead code for now (no callers); they'll come out in a follow-up dead-code cleanup round so this round's diff stays focused on the user-visible behavior change.

Editor app — local copy of the signed-out auth surface

The editor app has its own copy of the signed-out auth UI in frontend/editor_app/lib/modules/settings_build.dart (it doesn't go through AuthHub). It received the same restructure:

  • _buildSignedOutAccount — adds the "Login via third party" outlined button + "No account? Sign up via Casdoor" text link whenever widget.casdoorOrgLoginUrl is populated. Both navigate the browser via url_strategy.browserRedirect.
  • _legacyAuthBlock — drops the in-app Sign-up button. Now just a Login button + the contact-administrator caption.
  • _openSignUpDialog — deleted entirely (no callers).
  • _openLoginDialog — drops the onForgotPassword closure that used to open PasswordResetDialog.

Casdoor org-login URL plumbing

The URL ${endpoint}/login/${organization} is derived from the /api/v1/auth/casdoor/config/ response (which already returned both fields — see backend/creators/api.py:1372-1379). Each _AppShellState gains a String? _casdoorOrgLoginUrl field, populated inside the existing unawaited(getCasdoorConfig) probe in core/initial_data.dart, and threaded through to the per-app _SettingsPage(...) constructor as casdoorOrgLoginUrl. Editor passes it to its local _buildSignedOutAccount; planner and portal pass it to AuthHub.

String? so a backend in shadow mode (no CASDOOR_* env vars) keeps both the third-party CTAs collapsed.

Verification

  • flutter analyze from frontend/notechondria_shared/, frontend/editor_app/, frontend/planner_app/, frontend/portal_app/ — zero new errors / warnings (only the same pre-existing info-lints).

Carryover

  • RegistrationWizard, EmailCodeDialog, PasswordResetDialog remain in notechondria_shared as dead code. Their per-app callback fields (onRegister, onValidateInvitation, onVerify, onResendVerification, onRequestPasswordReset, onConfirmPasswordReset) also remain on each app's _SettingsPage constructor (unrendered, but still threaded through from app_shell.dart). A follow-up cleanup round can drop them — and the matching backend views in creators/api.py plus the matching AuthClient methods — once we're confident no out-of-tree client still calls the legacy register / verify / password-reset endpoints.
  • The BACKEND_VERSION / handshake readout placeholder on portal's _BackendSettingsPage (carryover from 0.1.101) is unchanged.

Operator runbook

After deploying 0.1.103:

  1. Sign in to https://auth.trance-0.com with the bootstrap admin.
  2. Open Organizations → notechondria → ensure "Account items" includes a signup-enabled email field, OR add at least one provider on the application's Providers tab so the org login page has a signup affordance.
  3. Visit any Notechondria frontend signed out — confirm the auth card now shows: Casdoor SSO pill, "Login via third party", "Sign up via Casdoor", and the email / password expander collapsed by default.