Sign in on the mail server's own page (OAuth with PKCE), sessions hold tokens; tenants on every edition

Contract C-8 and C-10: with OAUTH_CLIENT_SECRET set, sign-in goes through the
server's page and the session keeps sealed tokens, renewed before they expire,
instead of a password. Push keeps a credential that renews itself. A password
change signs the session out, since the server revokes its tokens. The mock
answers OAuth for tests and development. Eleven new strings, in all nine
catalogues.
This commit is contained in:
jcoffey-dev committed 2026-09-18 15:30:37 -07:00
1 parent c118184975
commit 8857bdac30
24 files changed
+1058 -74

No files matched your search

+3
View File
@@ -14,6 +14,7 @@ import { MAX_OBJECTS, MethodError, directory, enforceLimits, resolveRefs } from
import { handlers } from "./handlers.js";
export { account } from "./config.js";
import { checkOtp } from "./auth.js";
import { checkBearer, handleOAuth } from "./oauth.js";
import { sseClients, broadcast } from "./events.js";
/* ---------- http ---------- */
@@ -24,6 +25,7 @@ function unauthorized(res: ServerResponse) {
function checkAuth(req: IncomingMessage): boolean {
const h = req.headers.authorization ?? "";
if (checkBearer(h)) return true;
if (!h.startsWith("Basic ")) return false;
const raw = Buffer.from(h.slice(6), "base64").toString();
const sep = raw.indexOf(":");
@@ -77,6 +79,7 @@ const session = () => ({
/** Exported so tests can drive the mock in-process and shut it down. */
export const server = createServer(async (req, res) => {
const url = new URL(req.url ?? "/", `http://127.0.0.1:${PORT}`);
if (await handleOAuth(req, res, url)) return;
if (!checkAuth(req)) return unauthorized(res);
if (url.pathname === "/.well-known/jmap" || url.pathname === "/jmap/session") {
res.writeHead(200, { "content-type": "application/json" });