diff --git a/README.md b/README.md
index 0c8c516..d081570 100644
--- a/README.md
+++ b/README.md
@@ -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:
`` 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.
diff --git a/src/lib/oauthClientId.test.ts b/src/lib/oauthClientId.test.ts
index 4c0db44..922de26 100644
--- a/src/lib/oauthClientId.test.ts
+++ b/src/lib/oauthClientId.test.ts
@@ -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(
+ '',
+ );
+ expect(getOAuthClientId()).toBe('inbuxa-admin');
+ });
+
+ it('still prefers an injected client id when hosted apart from the server', async () => {
+ const getOAuthClientId = await loadWithMeta(
+ '',
+ );
+ expect(getOAuthClientId()).toBe('custom');
+ });
+
it('reads the document only once', async () => {
const getOAuthClientId = await loadWithMeta('');
expect(getOAuthClientId()).toBe('pocket-id-client');
diff --git a/src/lib/oauthClientId.ts b/src/lib/oauthClientId.ts
index ec125be..efe2144 100644
--- a/src/lib/oauthClientId.ts
+++ b/src/lib/oauthClientId.ts
@@ -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 , 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;
}