feat: add auth sso command for SSO provider configuration#484
feat: add auth sso command for SSO provider configuration#484moranshe-max merged 6 commits intomainfrom
auth sso command for SSO provider configuration#484Conversation
🚀 Package Preview Available!Install this PR's preview build with npm: npm i @base44-preview/cli@0.0.51-pr.484.9abd5e2Prefer not to change any import paths? Install using npm alias so your code still imports npm i "base44@npm:@base44-preview/cli@0.0.51-pr.484.9abd5e2"Or add it to your {
"dependencies": {
"base44": "npm:@base44-preview/cli@0.0.51-pr.484.9abd5e2"
}
}
Preview published to npm registry — try new features instantly! |
9e2128b to
aa29b02
Compare
Add `base44 auth sso enable/disable` command supporting Google, Microsoft, GitHub, Okta, and custom OIDC providers. Supports flags, --file JSON input, stdin, and env-file for secret resolution. SSO and social login are mutually exclusive — enabling one disables the other in the local config. Core SSO module is split into per-provider files with SSOSecretKey enum as single source of truth for API secret keys. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
aa29b02 to
4ecbb50
Compare
- Provider-specific validation errors now show CLI flag names (--tenant-id) instead of API key names (sso_tenant_id) - All validation errors include example commands with provider-specific required flags - Invalid provider error now includes a hint with example command Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…t validation Commander now enforces valid provider values early, so the manual check in validateProvider is no longer needed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4601bf3 to
7ce86f0
Compare
| return new Base44Command("sso") | ||
| .description( | ||
| "Configure SSO identity provider (google, microsoft, github, okta, custom)", | ||
| ) |
There was a problem hiding this comment.
SSO and social login are mutually exclusive — enabling SSO silently flips enableGoogleLogin / enableMicrosoftLogin / enableFacebookLogin / enableAppleLogin to false in auth/config.json (and vice versa for auth google enable etc.). This isn't surfaced anywhere user-facing. Consider mentioning it in the command description so it shows up in --help.
Paveltarno
left a comment
There was a problem hiding this comment.
nice work, this is shaping up well 👏
left some comments — main ones are around consistency with social-login (no lockout warning on disable, disable silently accepts enable flags) and using SchemaValidationError instead of hand-formatting zod issues. rest are mostly nits / wdyt
| const issues = result.error.issues | ||
| .map((i) => ` ${i.path.join(".")}: ${i.message}`) | ||
| .join("\n"); | ||
| throw new InvalidInputError( |
There was a problem hiding this comment.
all our other config-file loaders throw SchemaValidationError for zod parse failures — see agent/config.ts:25, function/config.ts:21, entity/config.ts:13, auth/config.ts:30. it formats the ZodError for us so we don't need to hand-roll the issues.map(...) block. wdyt about:
if (!result.success) {
throw new SchemaValidationError(
"Invalid SSO config file",
result.error,
filePath,
);
}(also called out in docs/error-handling.md)
| }; | ||
| } | ||
|
|
||
| async function ssoDisableAction({ |
There was a problem hiding this comment.
social-login.ts:150 warns the user via hasAnyLoginMethod when disabling leaves no login methods enabled, and hasAnyLoginMethod already factors in enableSSOLogin (schema.ts:74). does it make sense to do the same here? auth sso disable can lock users out the same way and right now it's silent.
| action: "enable" | "disable", | ||
| options: SSOOptions, | ||
| ): Promise<RunCommandResult> { | ||
| if (action === "disable") { |
There was a problem hiding this comment.
social-login.ts:73 errors out when enable-only options are passed alongside disable. here auth sso disable --provider google --client-id x --tenant-id y runs fine and silently drops everything. wdyt about doing the same check for parity?
| // -- File input schema (CLI concern: user-facing file format) ---------------- | ||
|
|
||
| const SSOConfigFileSchema = z.object({ | ||
| provider: z.enum(["google", "microsoft", "github", "okta", "custom"]), |
There was a problem hiding this comment.
the provider list here is hardcoded as z.enum(["google", "microsoft", "github", "okta", "custom"]) but KNOWN_SSO_PROVIDERS is already the single source of truth (and Commander uses it on sso.ts:284 via Object.values(KNOWN_SSO_PROVIDERS)). wdyt about deriving it the same way here? something like:
provider: z.enum(Object.values(KNOWN_SSO_PROVIDERS) as [KnownSSOProvider, ...KnownSSOProvider[]]),| } catch (error) { | ||
| if (error instanceof InvalidInputError) { | ||
| // Re-throw with CLI flag names and an example command | ||
| const flagMessage = error.message.replace(/sso_[a-z_]+/g, (key) => |
There was a problem hiding this comment.
the catch block here string-mangles sso_* substrings out of error.message to convert backend keys to flag names. similar to my comment on PR #445 — wdyt about returning structured info from buildSSOSecrets instead, so the CLI doesn't depend on the exact wording? e.g. attach missing: SSOSecretKey[] on the error, or split out a separate validateSSOSecrets() that returns missing keys without throwing. not blocking, just feels brittle.
| import { describe, it } from "vitest"; | ||
| import { fixture, setupCLITests } from "./testkit/index.js"; | ||
|
|
||
| describe("auth sso command", () => { |
There was a problem hiding this comment.
a few flows aren't covered — wdyt about adding:
--file <path>happy path + invalid JSON / schema error--env-fileresolvingsso_client_secret(and key-missing case)--client-secret-stdin- the flag-name translation in the error (asserting
--tenant-idappears, notsso_tenant_id) — this is the entire point of the catch inssoEnableActionand right now nothing locks it in - flag override of provider defaults (e.g. passing
--scope/--discovery-urlto google and asserting it overrides)
the existing enables google SSO with valid flags test could also assert what got written to disk + what setSecrets was called with — auth_social_login.spec.ts does this for its command.
| ): Promise<RunCommandResult> { | ||
| // Load file config if provided | ||
| let merged = options; | ||
| if (options.file) { |
There was a problem hiding this comment.
small thing — when both --file and --env-file are passed, --env-file only kicks in if merged.clientSecret is empty (sso.ts:156), so a clientSecret in the JSON file silently outranks the env file. the precedence isn't documented and a user/agent could reasonably expect either to win. wdyt about either erroring on the combo, or stating the precedence explicitly in --env-file's help text?
There was a problem hiding this comment.
good point, I went with failing when both are passed.
- Document SSO/social login mutual exclusivity in command description - Use SchemaValidationError for --file zod parse failures (matches other config loaders) - Warn when `auth sso disable` leaves no login methods enabled (parity with social-login) - Reject enable-only flags when used with `auth sso disable` (parity with social-login) - Derive --file zod enum from KNOWN_SSO_PROVIDERS instead of hardcoding - Replace string-mangling in catch block with structured MissingSSOFieldsError exposing typed missingKeys for the CLI to map to flag names - Document --env-file precedence vs --file in help text - Add test coverage for --file (happy path, invalid JSON, schema error), --env-file resolution + key-missing case, --client-secret-stdin, flag-name translation in errors, and --scope/--discovery-url override Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Per PR review feedback, prefer failing over silent precedence. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Paveltarno
left a comment
There was a problem hiding this comment.
hit a small bug while looking through the changes — left a comment.
|
|
||
| const providerNames = Object.keys(KNOWN_SSO_PROVIDERS); | ||
|
|
||
| /** Converts an API secret key like "sso_tenant_id" to a CLI flag like "--tenant-id". */ |
There was a problem hiding this comment.
secretKeyToFlag("sso_name") returns "--name" because the regex strips sso_ and then maps underscores to dashes, but the actual CLI flag declared on sso.ts:349 is --sso-name. so a user missing only --sso-name for the custom provider sees Missing required fields for custom: --name, then Commander rejects --name as an unknown option.
the existing flag-translation tests at test:86 / test:108 only cover --tenant-id and --okta-domain, both of which happen to derive correctly via the regex. sso_name is the lone secret key whose flag intentionally diverges from sso_* → --*, and there's no test for the custom missing---sso-name case so it slipped through.
…retKey.Name The regex-based conversion stripped sso_ from all keys, but `sso_name` intentionally keeps the prefix in its CLI flag (--sso-name) to avoid ambiguity. A custom provider missing only --sso-name surfaced an "unknown option" error to users. Replace the regex with an explicit Record<SSOSecretKey, string> map and add a regression test. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Paveltarno
left a comment
There was a problem hiding this comment.
🔥 nice work, all comments addressed cleanly
Note
Description
This PR adds the
base44 auth ssocommand, enabling users to configure SSO (Single Sign-On) authentication for their Base44 apps from the CLI. It supports five providers — Google, Microsoft, GitHub, Okta, and custom OIDC — and handles secret management by pushing credentials securely to the backend. The implementation includes both the CLI presentation layer (auth/sso.ts) and the core SDK layer with provider-specific schemas, secret key definitions, and operations.Related Issue
None
Type of Change
Changes Made
base44 auth sso enableandbase44 auth sso disablesubcommands under the existingauthcommand groupcore/resources/auth-config/sso/module with provider schemas for Google, Microsoft, GitHub, Okta, and custom OIDC providersbuildSSOSecrets,pushSSOSecrets,deleteSSOSecrets, andupdateSSOConfigoperations to manage secrets and local auth config--client-secret,--client-secret-stdin,--env-file, and environment variable (SSO_CLIENT_SECRET)--file, with CLI flags taking precedence--fileand--env-filecannot be used togetherauth.jsoncMissingSSOFieldsErrormaps missing API secret keys to user-facing flag names for clear error messagestests/cli/auth_sso.spec.ts) and core unit tests (tests/core/auth-sso.spec.ts) covering validation, all providers, and all secret input methodsTesting
npm test)Checklist
docs/(AGENTS.md) if I made architectural changesAdditional Notes
SSO and social login (Google, Microsoft, Facebook, Apple) are mutually exclusive in the auth config. Enabling SSO via this command automatically disables any enabled social login providers in the local
auth.jsonc. Thesso_namesecret key intentionally maps to--sso-name(not--name) to avoid future flag collisions.🤖 Generated by Claude | 2026-04-30 00:00 UTC | 9abd5e2