Skip to content

Commit 1329bc9

Browse files
committed
fix(sso): stop showing the redaction sentinel in the client secret field
1 parent 01f4b43 commit 1329bc9

1 file changed

Lines changed: 27 additions & 4 deletions

File tree

apps/sim/ee/sso/components/sso-settings.tsx

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import type { SsoRegistrationBody } from '@/lib/api/contracts/auth'
2323
import { useSession } from '@/lib/auth/auth-client'
2424
import { isEnterprise } from '@/lib/billing/plan-helpers'
2525
import { isBillingEnabled } from '@/lib/core/config/env-flags'
26+
import { REDACTED_MARKER } from '@/lib/core/security/redaction'
2627
import { getBaseUrl } from '@/lib/core/utils/urls'
2728
import { SettingsEmptyState } from '@/app/workspace/[workspaceId]/settings/components/settings-empty-state'
2829
import { SettingsPanel } from '@/app/workspace/[workspaceId]/settings/components/settings-panel'
@@ -144,6 +145,13 @@ function OrganizationSsoSettings({ organizationId }: SSOProps) {
144145
const [errors, setErrors] = useState<Record<string, string[]>>(DEFAULT_ERRORS)
145146
const [showErrors, setShowErrors] = useState(false)
146147

148+
/**
149+
* Editing an OIDC provider always means a secret is stored — the contract
150+
* requires one to register, and the API returns only its sentinel, never the
151+
* value. Leaving the field blank therefore means "keep it", not "clear it".
152+
*/
153+
const hasStoredClientSecret = isEditing && existingProvider?.providerType === 'oidc'
154+
147155
const hasChanges = (Object.keys(formData) as (keyof typeof formData)[]).some(
148156
(k) => formData[k] !== originalFormData[k]
149157
)
@@ -227,7 +235,9 @@ function OrganizationSsoSettings({ organizationId }: SSOProps) {
227235

228236
if (providerType === 'oidc') {
229237
newErrors.clientId = validateRequired('Client ID', data.clientId)
230-
newErrors.clientSecret = validateRequired('Client Secret', data.clientSecret)
238+
newErrors.clientSecret = hasStoredClientSecret
239+
? []
240+
: validateRequired('Client Secret', data.clientSecret)
231241
if (!data.scopes || !data.scopes.trim()) {
232242
newErrors.scopes = ['Scopes are required for OIDC providers']
233243
}
@@ -282,7 +292,12 @@ function OrganizationSsoSettings({ organizationId }: SSOProps) {
282292
image: OIDC_DEFAULT_MAPPING.image,
283293
},
284294
clientId: formData.clientId,
285-
clientSecret: formData.clientSecret,
295+
// Blank on an edit means the admin did not retype it: send the
296+
// sentinel so the server keeps the stored secret.
297+
clientSecret:
298+
hasStoredClientSecret && !formData.clientSecret
299+
? REDACTED_MARKER
300+
: formData.clientSecret,
286301
scopes: formData.scopes.split(',').map((s) => s.trim()),
287302
...(formData.authorizationEndpoint.trim()
288303
? { authorizationEndpoint: formData.authorizationEndpoint.trim() }
@@ -373,7 +388,10 @@ function OrganizationSsoSettings({ organizationId }: SSOProps) {
373388
if (existingProvider.providerType === 'oidc' && existingProvider.oidcConfig) {
374389
const config = JSON.parse(existingProvider.oidcConfig)
375390
clientId = config.clientId || ''
376-
clientSecret = config.clientSecret || ''
391+
// The API returns the sentinel, never the secret. Showing it verbatim put
392+
// the literal "[REDACTED]" in the field; blanking it lets the placeholder
393+
// say a secret is stored, and submit re-sends the sentinel to keep it.
394+
clientSecret = config.clientSecret === REDACTED_MARKER ? '' : config.clientSecret || ''
377395
scopes = config.scopes?.join(',') || 'openid,profile,email'
378396
mapping = config.mapping ?? {}
379397
authorizationEndpoint = config.authorizationEndpoint || ''
@@ -665,6 +683,11 @@ function OrganizationSsoSettings({ organizationId }: SSOProps) {
665683

666684
<SettingRow
667685
label='Client Secret'
686+
description={
687+
hasStoredClientSecret
688+
? 'Your saved secret is never shown again. Leave this blank to keep it, or type a new one to replace it.'
689+
: undefined
690+
}
668691
error={
669692
showErrors && errors.clientSecret.length > 0
670693
? errors.clientSecret.join(' ')
@@ -674,7 +697,7 @@ function OrganizationSsoSettings({ organizationId }: SSOProps) {
674697
<ChipInput
675698
id='sso-client-secret'
676699
type='text'
677-
placeholder='Enter Client Secret'
700+
placeholder={hasStoredClientSecret ? '••••••••••••' : 'Enter Client Secret'}
678701
value={formData.clientSecret}
679702
name='sso_client_key'
680703
autoComplete='off'

0 commit comments

Comments
 (0)