Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion packages/auth/src/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,19 @@ export function workspaceDomains(): readonly string[] {
}

export function primaryWorkspaceDomain(): string | undefined {
return allowList().domains[0];
const { domains, addresses } = allowList();

if (domains[0]) return domains[0];

// An address-only allow list still names a workspace domain, and a solo

@cubic-dev-ai cubic-dev-ai Bot Sep 10, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: AGENTS.md forbids all code comments ("Never add code comments. Not to new code, not to code you edit."), and this change adds a four-line comment block in production code. Remove the comment; the branch logic is self-explanatory. The added comment in packages/auth/test/workspace.spec.ts (// hd narrows the chooser to one domain...) violates the same rule.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/auth/src/workspace.ts, line 40:

<comment>AGENTS.md forbids all code comments ("Never add code comments. Not to new code, not to code you edit."), and this change adds a four-line comment block in production code. Remove the comment; the branch logic is self-explanatory. The added comment in packages/auth/test/workspace.spec.ts (`// hd narrows the chooser to one domain...`) violates the same rule.</comment>

<file context>
@@ -33,7 +33,19 @@ export function workspaceDomains(): readonly string[] {
+
+	if (domains[0]) return domains[0];
+
+	// An address-only allow list still names a workspace domain, and a solo
+	// self-hoster on `me@acme.com` wants the same account chooser as one on
+	// `acme.com`. Only when every allowed address shares a domain: `hd` narrows
</file context>
Fix with cubic

// self-hoster on `me@acme.com` wants the same account chooser as one on
// `acme.com`. Only when every allowed address shares a domain: `hd` narrows
// the chooser to one, so sending it for one of several would hide the rest.
const hosts = new Set(
addresses.map((address) => address.split("@")[1]).filter(Boolean),

@cubic-dev-ai cubic-dev-ai Bot Sep 10, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When an address entry has no host, .filter(Boolean) drops it and a valid sibling can produce hd even though not all entries share a domain. Preserve the empty host or reject derivation whenever any address lacks a host.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/auth/src/workspace.ts, line 45:

<comment>When an address entry has no host, `.filter(Boolean)` drops it and a valid sibling can produce `hd` even though not all entries share a domain. Preserve the empty host or reject derivation whenever any address lacks a host.</comment>

<file context>
@@ -33,7 +33,19 @@ export function workspaceDomains(): readonly string[] {
+	// `acme.com`. Only when every allowed address shares a domain: `hd` narrows
+	// the chooser to one, so sending it for one of several would hide the rest.
+	const hosts = new Set(
+		addresses.map((address) => address.split("@")[1]).filter(Boolean),
+	);
+
</file context>
Suggested change
addresses.map((address) => address.split("@")[1]).filter(Boolean),
addresses.map((address) => address.split("@")[1]),
Fix with cubic

);

return hosts.size === 1 ? [...hosts][0] : undefined;

@cubic-dev-ai cubic-dev-ai Bot Sep 10, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When an address-only allow-list rejects other@acme.com, this derived value makes the guard say Sign in with your @acme.com account even though that address is refused. Keep the Google hd derivation separate from the rejection-message domain, or use the generic allow-list error for address-only lists.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/auth/src/workspace.ts, line 48:

<comment>When an address-only allow-list rejects `other@acme.com`, this derived value makes the guard say `Sign in with your @acme.com account` even though that address is refused. Keep the Google `hd` derivation separate from the rejection-message domain, or use the generic allow-list error for address-only lists.</comment>

<file context>
@@ -33,7 +33,19 @@ export function workspaceDomains(): readonly string[] {
+		addresses.map((address) => address.split("@")[1]).filter(Boolean),
+	);
+
+	return hosts.size === 1 ? [...hosts][0] : undefined;
 }
 
</file context>
Fix with cubic

}

export function hasSignInAllowList(): boolean {
Expand Down
44 changes: 44 additions & 0 deletions packages/auth/test/workspace.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { afterEach, describe, expect, it } from "bun:test";
import { isWorkspaceEmail, primaryWorkspaceDomain } from "../src/workspace";

const original = process.env.ALLOWED_SIGN_IN;

afterEach(() => {
if (original === undefined) delete process.env.ALLOWED_SIGN_IN;
else process.env.ALLOWED_SIGN_IN = original;
});

describe("the domain behind the account chooser", () => {
it("is the configured domain when one is configured", () => {
process.env.ALLOWED_SIGN_IN = "acme.com";
expect(primaryWorkspaceDomain()).toBe("acme.com");
});

it("is derived from a single address, so a solo self-hoster gets the hint too", () => {
process.env.ALLOWED_SIGN_IN = "rep@acme.com";
expect(primaryWorkspaceDomain()).toBe("acme.com");
});

it("is withheld when the addresses span more than one domain", () => {
// `hd` narrows the chooser to one domain, so sending it for one of two
// would hide the other rather than help.
process.env.ALLOWED_SIGN_IN = "rep@acme.com,other@beta.com";
expect(primaryWorkspaceDomain()).toBeUndefined();
});

it("still prefers a configured domain over an address", () => {
process.env.ALLOWED_SIGN_IN = "rep@beta.com,acme.com";
expect(primaryWorkspaceDomain()).toBe("acme.com");
});

it("is nothing when the list is empty, which fails closed", () => {
process.env.ALLOWED_SIGN_IN = "";
expect(primaryWorkspaceDomain()).toBeUndefined();
});

it("does not widen who may sign in", () => {
process.env.ALLOWED_SIGN_IN = "rep@acme.com";
expect(isWorkspaceEmail("rep@acme.com")).toBe(true);
expect(isWorkspaceEmail("someone-else@acme.com")).toBe(false);
});
});