Skip to content

Support multiple allowed email domains for portal OIDC (OIDC_ALLOWED_EMAIL_DOMAINS) #672

Description

@muei

Summary

The portal's OIDC trust boundary only supports a single email domain
(OIDC_ALLOWED_EMAIL_DOMAIN, checked as email.endsWith("@<domain>")). We need
to allow sign-in from two org domains (two separate org domains).
Today the only way to cover two domains is OIDC_ALLOWED_EMAILS, which requires
enumerating every individual account — fragile as people join.

What we'd like

A comma-separated OIDC_ALLOWED_EMAIL_DOMAINS (plural), keeping the existing
singular OIDC_ALLOWED_EMAIL_DOMAIN for backward compatibility. Both should
feed the same domain check, and the production trust-boundary validation should
count the plural list as satisfying the boundary requirement.

Patch we're carrying locally (for reference)

We've been running this as a local patch; happy to hand it over or let you
implement it however you prefer.

--- a/plugins/portal/src/oidc.ts
+++ b/plugins/portal/src/oidc.ts
@@ export interface PrincipalRule {
   claim: "sub" | "email";
   allowedEmailDomain?: string;
+  allowedEmailDomains?: readonly string[];
   allowedEmails?: readonly string[];
 }
@@ resolvePrincipal:
-  if (rule.allowedEmailDomain) {
-    const domain = rule.allowedEmailDomain.toLowerCase();
-    if (!email.endsWith(`@${domain}`)) throw new Error("account is outside the permitted domain");
-    const hd = args.userinfo.hd ?? args.claims.hd;
-    if (typeof hd === "string" && hd.toLowerCase() !== domain)
-      throw new Error("account is outside the permitted domain");
-  }
+  const allowedDomains = [
+    ...(rule.allowedEmailDomain ? [rule.allowedEmailDomain] : []),
+    ...(rule.allowedEmailDomains ?? []),
+  ].map((domain) => domain.toLowerCase());
+  if (allowedDomains.length) {
+    if (!allowedDomains.some((domain) => email.endsWith(`@${domain}`)))
+      throw new Error("account is outside the permitted domain");
+    const hd = args.userinfo.hd ?? args.claims.hd;
+    if (typeof hd === "string" && !allowedDomains.includes(hd.toLowerCase()))
+      throw new Error("account is outside the permitted domain");
+  }
--- a/plugins/portal/src/index.ts
+++ b/plugins/portal/src/index.ts
@@ const PRINCIPAL_RULE: PrincipalRule = {
   claim: (process.env.OIDC_PRINCIPAL_CLAIM ?? "email") as PrincipalRule["claim"],
   allowedEmailDomain: process.env.OIDC_ALLOWED_EMAIL_DOMAIN || undefined,
+  allowedEmailDomains: process.env.OIDC_ALLOWED_EMAIL_DOMAINS?.split(",")
+    .map((domain) => domain.trim())
+    .filter(Boolean),
   allowedEmails: process.env.OIDC_ALLOWED_EMAILS?.split(",")
     .map((email) => email.trim())
     .filter(Boolean),
 };

Plus three validation tweaks: the "requires OIDC_PRINCIPAL_CLAIM=email" check,
a new "OIDC_ALLOWED_EMAIL_DOMAINS must be a comma-separated list of valid
domains" check, and the production trust-boundary check all include
allowedEmailDomains.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions