Sign in as inbuxa-admin when hosted apart from the server (inbuxa-server contract C-6)

This commit is contained in:
2026-09-18 13:05:04 -07:00
parent e387e7976f
commit 2399f5ce97
3 changed files with 34 additions and 4 deletions
+4 -1
View File
@@ -22,7 +22,10 @@ server has without hardcoding any of them.
mail server itself. INBUXA Admin is its own deployment, pointed at the server
either at build time (`VITE_API_BASE_URL`) or at deploy time:
`<meta name="api-base-url" content="https://mail.example.com">` in
`index.html`.
`index.html`. Hosted like that, it signs in as the OAuth client
`inbuxa-admin`, which the server registers when it's started with
`INBUXA_ADMIN_URL` set to INBUXA Admin's address (for development,
`http://localhost:5173`).
- **INBUXA's look:** the logo and ihasmail's palette.
- **Two-factor setup** names INBUXA as the issuer, and no longer makes
authenticator apps fetch a logo from a third-party site.
+14
View File
@@ -46,6 +46,20 @@ describe('getOAuthClientId', () => {
expect(getOAuthClientId()).toBe('stalwart-webui');
});
it('is inbuxa-admin when hosted apart from the server', async () => {
const getOAuthClientId = await loadWithMeta(
'<meta name="oauth-client-id" content="" /><meta name="api-base-url" content="https://mail.example.org" />',
);
expect(getOAuthClientId()).toBe('inbuxa-admin');
});
it('still prefers an injected client id when hosted apart from the server', async () => {
const getOAuthClientId = await loadWithMeta(
'<meta name="oauth-client-id" content="custom" /><meta name="api-base-url" content="https://mail.example.org" />',
);
expect(getOAuthClientId()).toBe('custom');
});
it('reads the document only once', async () => {
const getOAuthClientId = await loadWithMeta('<meta name="oauth-client-id" content="pocket-id-client" />');
expect(getOAuthClientId()).toBe('pocket-id-client');
+16 -3
View File
@@ -4,14 +4,27 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
const DEFAULT_CLIENT_ID = 'stalwart-webui';
// INBUXA requires OAuth clients to be registered, and registers these two on
// every start (inbuxa-server contract C-6). Served by the server itself, this
// is the web interface at /admin, registered as `stalwart-webui`. Hosted
// anywhere else, with the server's address in <meta name="api-base-url">, it
// is INBUXA Admin, registered as `inbuxa-admin` from INBUXA_ADMIN_URL.
const SERVED_BY_SERVER_CLIENT_ID = 'stalwart-webui';
const HOSTED_ELSEWHERE_CLIENT_ID = 'inbuxa-admin';
let cached: string | undefined;
function metaContent(name: string): string | undefined {
return document.querySelector(`meta[name="${name}"]`)?.getAttribute('content')?.trim() || undefined;
}
export function getOAuthClientId(): string {
if (cached !== undefined) return cached;
const injected = document.querySelector('meta[name="oauth-client-id"]')?.getAttribute('content')?.trim();
cached = injected ? injected : DEFAULT_CLIENT_ID;
const hostedElsewhere = Boolean(
(import.meta.env.VITE_API_BASE_URL as string | undefined) || metaContent('api-base-url'),
);
cached =
metaContent('oauth-client-id') ?? (hostedElsewhere ? HOSTED_ELSEWHERE_CLIENT_ID : SERVED_BY_SERVER_CLIENT_ID);
return cached;
}