Summary
Email-verification signup cannot be completed. Clicking the link in the verification email lands the user on a raw JSON response:
{"success":true,"data":{"success":true,"email":"someone@example.org","role":40,"roleName":"EDITOR"}}
Three defects compound, and the flow dead-ends regardless of which one you work around.
Affected: @emdash-cms/auth@0.32.0 / emdash@0.32.0, both verified unchanged in 0.37.0 / 0.36.0 (current latest).
1. The email links to the API route, not the admin page
packages/auth/src/signup.ts:94:
const url = new URL("/_emdash/api/auth/signup/verify", config.baseUrl);
url.searchParams.set("token", token);
/_emdash/api/auth/signup/verify is the endpoint the SPA calls; it returns JSON for the UI to render. Sending a human there shows them the JSON above.
The invite flow, in the same package, does it correctly — packages/auth/src/invite.ts:72:
const url = new URL(`${config.baseUrl}/admin/invite/accept`);
The signup page already handles a token from the URL, so it is the right destination:
// admin SignupPage
const urlToken = new URLSearchParams(window.location.search).get("token");
if (urlToken) { setToken(urlToken); verifyToken(urlToken); }
2. /_emdash/admin/signup is not a public route
packages/core/src/astro/middleware/auth.ts:331:
url.pathname.startsWith("/_emdash/admin/login") ||
url.pathname.startsWith("/_emdash/admin/invite/accept");
/_emdash/admin/signup is absent, so an unauthenticated visitor is redirected to login:
$ curl -sI 'https://example.com/_emdash/admin/signup?token=…'
HTTP/2 302
location: https://example.com/_emdash/admin/login?redirect=%2F_emdash%2Fadmin%2Fsignup
The page is reachable today only by client-side navigation from the login page ("Don't have an account? Sign up"), which is how a user requests signup in the first place — but never with a token in the URL.
3. The login redirect drops the query string
packages/core/src/astro/middleware/auth.ts:684:
loginUrl.searchParams.set("redirect", url.pathname);
Only pathname, so ?token=… is lost. Fixing (1) and (2) alone would still lose the token for anyone who hits the redirect.
Reproduction
- Enable email/password or magic-link signup.
- From
/_emdash/admin/login, follow "Sign up" and submit an address.
- Open the verification email and click the link.
- Raw JSON. Editing the URL by hand to
/_emdash/admin/signup?token=… redirects to login and drops the token.
Impact
Self-signup cannot complete on a fresh instance. The invite flow is unaffected — its page is public and its email links to it — so inviting users is the working path, but that is not obvious from the failure, which looks like a broken deployment rather than an unsupported flow.
Suggested fix
Point the email at the page, matching invite.ts:
const url = new URL(`${config.baseUrl}/admin/signup`);
url.searchParams.set("token", token);
Add the route to the public list:
url.pathname.startsWith("/_emdash/admin/login") ||
url.pathname.startsWith("/_emdash/admin/signup") ||
url.pathname.startsWith("/_emdash/admin/invite/accept");
And preserve the query string on the redirect:
loginUrl.searchParams.set("redirect", url.pathname + url.search);
The third is worth doing independently of the other two — any future token-bearing admin URL hits the same loss. Happy to open a PR.
Summary
Email-verification signup cannot be completed. Clicking the link in the verification email lands the user on a raw JSON response:
{"success":true,"data":{"success":true,"email":"someone@example.org","role":40,"roleName":"EDITOR"}}Three defects compound, and the flow dead-ends regardless of which one you work around.
Affected:
@emdash-cms/auth@0.32.0/emdash@0.32.0, both verified unchanged in0.37.0/0.36.0(currentlatest).1. The email links to the API route, not the admin page
packages/auth/src/signup.ts:94:/_emdash/api/auth/signup/verifyis the endpoint the SPA calls; it returns JSON for the UI to render. Sending a human there shows them the JSON above.The invite flow, in the same package, does it correctly —
packages/auth/src/invite.ts:72:The signup page already handles a token from the URL, so it is the right destination:
2.
/_emdash/admin/signupis not a public routepackages/core/src/astro/middleware/auth.ts:331:/_emdash/admin/signupis absent, so an unauthenticated visitor is redirected to login:The page is reachable today only by client-side navigation from the login page ("Don't have an account? Sign up"), which is how a user requests signup in the first place — but never with a token in the URL.
3. The login redirect drops the query string
packages/core/src/astro/middleware/auth.ts:684:Only
pathname, so?token=…is lost. Fixing (1) and (2) alone would still lose the token for anyone who hits the redirect.Reproduction
/_emdash/admin/login, follow "Sign up" and submit an address./_emdash/admin/signup?token=…redirects to login and drops the token.Impact
Self-signup cannot complete on a fresh instance. The invite flow is unaffected — its page is public and its email links to it — so inviting users is the working path, but that is not obvious from the failure, which looks like a broken deployment rather than an unsupported flow.
Suggested fix
Point the email at the page, matching
invite.ts:Add the route to the public list:
And preserve the query string on the redirect:
The third is worth doing independently of the other two — any future token-bearing admin URL hits the same loss. Happy to open a PR.