Skip to content

Per-environment runtime: POST /api/v1/auth/set-initial-password → 404 (recovery flow dead-ends) #1544

Description

@xuyushun441-sys

Symptom

On a per-environment ObjectOS runtime (cloud distribution, *.objectos.app/*.objectos.ai), after an owner enters via SSO-as-owner, the auth-proxy redirects them (no local credential yet) to /_console/system/profile?recovery_needed=true to "Set a recovery password". Submitting that form POSTs /api/v1/auth/set-initial-password and gets 404 — the recovery flow dead-ends and the user is stuck.

Empirical confirmation (staging crm.objectos.app)

POST /api/v1/auth/set-initial-password  → 404   (route not found)
GET  /api/v1/auth/get-session           → 200   (better-auth IS mounted & responding)

So better-auth itself is fine; the specific route is simply absent on the runtime.

Root cause

  • set-initial-password is a custom route registered by the full AuthPluginpackages/plugins/plugin-auth/src/auth-plugin.ts (rawApp.post(\${basePath}/set-initial-password`, …)`, ~L483).
  • On a per-environment kernel the AuthPlugin is skipped ("⚠ AuthPlugin skipped on host kernel — runtime mode (ObjectOSEnvironmentPlugin detected)"). Auth there is served by packages/runtime/src/cloud/auth-proxy-plugin.ts, which special-cases sso-handoff-issue and sso-exchange and otherwise forwards /api/v1/auth/* to better-auth. better-auth has no set-initial-password route → 404.

Net: the recovery flow (auth-proxy redirect → "Set local password") promises an endpoint that only exists on the host-kernel AuthPlugin, not on the runtime. It can never have worked on a per-environment runtime.

Suggested fix

Add a set-initial-password handler to runtime/src/cloud/auth-proxy-plugin.ts (alongside the existing sso-exchange special-case), mirroring the AuthPlugin logic against the environment's auth service (which is already resolvable there — get-session works, so getAuthContext()internalAdapter + password.hash + createAccount are available):

  1. require a valid session (else 401),
  2. reject if a credential account with a password already exists (409 → "use change-password"),
  3. enforce min/max length,
  4. internalAdapter.createAccount({ userId, providerId: 'credential', accountId: userId, password: hash }).

This is the same body as auth-plugin.ts L483–536; factoring it into a shared helper both call sites can reuse would avoid drift.

Impact / context

Exposed now that per-environment runtimes boot cleanly (the prior LocalCryptoProvider crash-loop masked it — the runtime never stayed up long enough to reach the recovery page). Blocks the "Open environment as admin" → first-time owner UX end-to-end. framework @ 654a2fd9 (also present at the cloud-pinned cf03ef2f7).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions