Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
claude/adoring-hopper-5x74bqclaude/affectionate-feynman-ykrf1hclaude/architecture-audit-design-wxprenclaude/build-status-update-3MXsfclaude/charming-meitner-mllb5rclaude/compare-gate-gluecron-s4mFQclaude/confident-faraday-tikcwbclaude/continue-work-XMTlIclaude/crontech-gluecron-deploy-7MIECclaude/crontech-platform-setup-SeKfwclaude/design-2026claude/ecstatic-ptolemy-jMdigclaude/enhance-github-integration-QNHdGclaude/fix-aa-loop-issue-PonMQclaude/fix-actions-and-processclaude/fix-desktop-errors-XqoW8claude/fix-red-workflowsclaude/fix-website-access-6FKJNclaude/gatetest-integration-hardeningclaude/github-audit-improvements-bDFr9claude/gluecron-launch-status-FoMRlclaude/hopeful-lamport-olfCTclaude/issue-to-pr-and-protectionsclaude/jolly-heisenberg-2sg1Qclaude/launch-preparation-QmTb6claude/new-session-xk1l7claude/plan-platform-architecture-kkN4yclaude/platform-analysis-roadmap-1nUGLclaude/platform-launch-assessment-8dWV8claude/polish-platform-release-AeDrUclaude/resume-previous-work-KzyLwclaude/review-crontech-handoff-qYEVqclaude/review-project-completeness-lHhS2claude/review-readme-docs-ulqPKclaude/serene-edison-rj87weclaude/setup-multi-repo-dev-BCwNQclaude/ship-fixes-and-tests-Jvz1cclaude/site-audit-competitive-pctlwgclaude/site-migration-vercel-XstpKclaude/standalone-product-repos-XHFTDcopilot/feat-smart-empty-states-keyboard-first-enhancementcopilot/feat-smart-morning-digest-review-context-restorecopilot/fix-and-process-workflowscopilot/update-ai-powered-code-reviewfeat/debt-mapfeat/push-policy-codeowners-hardeningfeat/smart-digest-contextfeat/stage-impactfeat/t1-secret-migrationfeat/u-polishfeat/w-self-hostfeat/w2-claude-configfix/agent-journey-orphan-sweepgatetest/auto-fix-1776586424172gatetest/auto-fix-1776586534814gatetest/auto-fix-1776590685143gatetest/auto-fix-1776590808199mainops/redeploy-retriggerstyle/dxt-cta-themeworktree-agent-a3377aad30d55da26worktree-agent-a7ef607b7ee1d6c74
google-oauth-env.test.ts4.4 KB · 133 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import { describe, expect, test } from "bun:test";
import {
  googleOauthConfigFromEnv,
  resolveGoogleOauthConfig,
} from "../lib/sso";
import type { SsoConfig } from "../db/schema";

/** Minimal SsoConfig row builder for precedence tests. */
function row(overrides: Partial<SsoConfig>): SsoConfig {
  const now = new Date();
  return {
    id: "google",
    enabled: false,
    providerName: "Google",
    issuer: "https://accounts.google.com",
    authorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth",
    tokenEndpoint: "https://oauth2.googleapis.com/token",
    userinfoEndpoint: "https://openidconnect.googleapis.com/v1/userinfo",
    clientId: null,
    clientSecret: null,
    scopes: "openid email profile",
    allowedEmailDomains: null,
    autoCreateUsers: true,
    createdAt: now,
    updatedAt: now,
    ...overrides,
  } as SsoConfig;
}

const ENV_CFG = googleOauthConfigFromEnv({
  GOOGLE_OAUTH_CLIENT_ID: "env-id.apps.googleusercontent.com",
  GOOGLE_OAUTH_CLIENT_SECRET: "env-secret",
});

describe("googleOauthConfigFromEnv", () => {
  test("returns null when credentials are absent", () => {
    expect(googleOauthConfigFromEnv({})).toBeNull();
    expect(
      googleOauthConfigFromEnv({ GOOGLE_OAUTH_CLIENT_ID: "id-only" })
    ).toBeNull();
    expect(
      googleOauthConfigFromEnv({ GOOGLE_OAUTH_CLIENT_SECRET: "secret-only" })
    ).toBeNull();
    expect(
      googleOauthConfigFromEnv({
        GOOGLE_OAUTH_CLIENT_ID: "  ",
        GOOGLE_OAUTH_CLIENT_SECRET: "s",
      })
    ).toBeNull();
  });

  test("builds an enabled config from the env pair", () => {
    const cfg = googleOauthConfigFromEnv({
      GOOGLE_OAUTH_CLIENT_ID: "abc.apps.googleusercontent.com",
      GOOGLE_OAUTH_CLIENT_SECRET: "shh",
    });
    expect(cfg).not.toBeNull();
    expect(cfg!.enabled).toBe(true);
    expect(cfg!.id).toBe("google");
    expect(cfg!.clientId).toBe("abc.apps.googleusercontent.com");
    expect(cfg!.clientSecret).toBe("shh");
    expect(cfg!.authorizationEndpoint).toBe(
      "https://accounts.google.com/o/oauth2/v2/auth"
    );
    expect(cfg!.tokenEndpoint).toBe("https://oauth2.googleapis.com/token");
    expect(cfg!.userinfoEndpoint).toBe(
      "https://openidconnect.googleapis.com/v1/userinfo"
    );
    expect(cfg!.scopes).toBe("openid email profile");
    expect(cfg!.autoCreateUsers).toBe(true);
    expect(cfg!.allowedEmailDomains).toBeNull();
  });

  test("honours optional knobs", () => {
    const cfg = googleOauthConfigFromEnv({
      GOOGLE_OAUTH_CLIENT_ID: "id",
      GOOGLE_OAUTH_CLIENT_SECRET: "secret",
      GOOGLE_OAUTH_AUTO_CREATE: "0",
      GOOGLE_OAUTH_ALLOWED_DOMAINS: "example.com,corp.example.com",
    });
    expect(cfg!.autoCreateUsers).toBe(false);
    expect(cfg!.allowedEmailDomains).toBe("example.com,corp.example.com");
  });
});

describe("resolveGoogleOauthConfig — precedence", () => {
  test("an enabled, fully-credentialed admin row wins over env", () => {
    const dbRow = row({
      enabled: true,
      clientId: "db-id",
      clientSecret: "db-secret",
    });
    const live = resolveGoogleOauthConfig(dbRow, ENV_CFG);
    expect(live).toBe(dbRow);
    expect(live!.clientId).toBe("db-id");
  });

  test("a DISABLED credentialed row does NOT shadow the env bootstrap", () => {
    // Regression: a half-finished /admin/google-oauth save (creds entered,
    // Enable left off) used to suppress a working GOOGLE_OAUTH_* bootstrap,
    // leaving "Sign in with Google" dark with a misleading "not enabled".
    const dbRow = row({
      enabled: false,
      clientId: "db-id",
      clientSecret: "db-secret",
    });
    const live = resolveGoogleOauthConfig(dbRow, ENV_CFG);
    expect(live).toBe(ENV_CFG);
    expect(live!.enabled).toBe(true);
  });

  test("a credential-less row never shadows the env bootstrap", () => {
    const live = resolveGoogleOauthConfig(row({ enabled: true }), ENV_CFG);
    expect(live).toBe(ENV_CFG);
  });

  test("env bootstrap alone (no DB row) is live", () => {
    expect(resolveGoogleOauthConfig(null, ENV_CFG)).toBe(ENV_CFG);
  });

  test("with no env, a disabled row is returned as-is (caller gates on it)", () => {
    const dbRow = row({
      enabled: false,
      clientId: "db-id",
      clientSecret: "db-secret",
    });
    expect(resolveGoogleOauthConfig(dbRow, null)).toBe(dbRow);
  });

  test("nothing configured anywhere resolves to null", () => {
    expect(resolveGoogleOauthConfig(null, null)).toBeNull();
  });
});