No address step before signing in when there's only one mail server

The server's own page asks for the username, so with a single server the
sign-in page keeps only the own-device choice. With several servers the
address still comes first, since its domain picks the server. One new string,
in all nine catalogues.
This commit is contained in:
2026-09-18 15:51:54 -07:00
parent 8857bdac30
commit cdd8fabff9
14 changed files with 53 additions and 6 deletions
+4 -1
View File
@@ -41,7 +41,7 @@ import {
revokeAppPassword,
} from "./account.js";
import { imageProxyHandler } from "./imageproxy.js";
import { SignInError, finish as finishSignIn, needsRefresh, oauthEnabled, refreshTokens, start as startSignIn, type TokenSet } from "./oauth.js";
import { SignInError, finish as finishSignIn, needsRefresh, oauthEnabled, refreshTokens, singleServer, start as startSignIn, type TokenSet } from "./oauth.js";
import { icsProxyHandler } from "./icsproxy.js";
import { staticHandler } from "./static.js";
@@ -384,6 +384,9 @@ export function createApp(basePath = config.basePath): Hono<Env> {
settingsPolicy: config.settingsPolicy,
/* "oauth": sign in on the mail server's own page (see oauth.ts). */
signIn: oauthEnabled() ? "oauth" : "password",
/* With "oauth": true when the server's page can take it from here, so
the sign-in form doesn't ask for an address first. */
signInDirect: oauthEnabled() && singleServer(),
}),
);
+12
View File
@@ -84,6 +84,18 @@ after(() => {
test("the configuration tells the web app to use the server's page", async () => {
const body = await jsonOf(await call("/api/config"));
assert.equal(body.signIn, "oauth");
assert.equal(body.signInDirect, true, "one mail server: its page asks for the username, not ihasmail");
});
test("with one mail server, sign-in starts without an address", async () => {
const res = await call("/api/auth/oauth/start");
assert.equal(res.status, 302);
const to = new URL(res.headers.get("location")!);
assert.equal(to.searchParams.has("login_hint"), false);
const approved = await fetch(to, { redirect: "manual" });
const back = new URL(approved.headers.get("location")!);
assert.equal((await call(`/api/auth/callback${back.search}`)).headers.get("location"), "/");
assert.equal((await call("/api/auth/session")).status, 200);
});
test("the password form is refused: ihasmail never sees a password", async () => {
+10
View File
@@ -52,6 +52,16 @@ export function oauthEnabled(): boolean {
return Boolean(config.oauthClientSecret);
}
/**
* Whether every account is on the same server. Then sign-in needs no address
* first: the server's page asks for the username itself. With several servers
* (STALWART_SERVERS_FILE), the domain picks the server, so the address comes
* first.
*/
export function singleServer(): boolean {
return Object.values(config.stalwartServers).every((url) => url === config.stalwartUrl);
}
/** The one redirect URI registered for this client on the server. */
export function redirectUri(): string {
return `${config.publicUrl}${config.basePath}/api/auth/callback`;