UIPackage
Menu

Framework

Change language

Boilerplate repo

Sso Identity Provider Config

blocksecurity

Enterprise Single Sign-On (SAML 2.0 / OIDC) identity provider configuration wizard with IdP preset picker, SP credentials, XML metadata dropzone, X.509 cert PEM validator, domain enforcement switch, and interactive SAML test runner.

Also available for Vue ->

Installation

$npx shadcn@latest add https://uipkge.dev/r/react/sso-identity-provider-config.json
Named registry:npx shadcn@latest add @uipkge-react/sso-identity-provider-configInstalls to:components/blocks/

Variants

Loading interactive previews…

Props

NameType / ValuesDefaultRequired
initialSsoEnabledbooleanoptional
initialPresetIdpPresetIdoptional
initialEnforceDomainbooleanoptional
classNamestringoptional

Schema

Type aliases exported from this item's source. Use these to shape the data you pass in.

IdpPreset
interface IdpPreset {
  id: IdpPresetId
  name: string
  subtitle: string
  badge: string
  defaultSsoUrl: string
  defaultEntityId: string
  domain: string
  defaultCert: string
}
ClaimAttribute
interface ClaimAttribute {
  samlAttribute: string
  mappedField: string
  sampleValue: string
}

Files installed (1)

  • components/blocks/SsoIdentityProviderConfig.tsx35.5 kB
    'use client'
    
    import * as React from 'react'
    import {
      Check,
      CheckCircle2,
      Copy,
      Download,
      ExternalLink,
      FileCode,
      Globe,
      KeyRound,
      Lock,
      Play,
      RotateCw,
      Save,
      ShieldAlert,
      ShieldCheck,
      UploadCloud,
      UserCheck,
      X,
    } from 'lucide-react'
    import { Badge } from '@/components/ui/badge'
    import { Button } from '@/components/ui/button'
    import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
    import {
      Dialog,
      DialogContent,
      DialogDescription,
      DialogFooter,
      DialogHeader,
      DialogTitle,
    } from '@/components/ui/dialog'
    import { Input } from '@/components/ui/input'
    import { Separator } from '@/components/ui/separator'
    import { Switch } from '@/components/ui/switch'
    import { Textarea } from '@/components/ui/textarea'
    import { cn } from '@/lib/utils'
    
    export type IdpPresetId = 'okta' | 'entra' | 'google' | 'custom'
    
    export interface IdpPreset {
      id: IdpPresetId
      name: string
      subtitle: string
      badge: string
      defaultSsoUrl: string
      defaultEntityId: string
      domain: string
      defaultCert: string
    }
    
    export interface ClaimAttribute {
      samlAttribute: string
      mappedField: string
      sampleValue: string
    }
    
    export interface SsoIdentityProviderConfigProps {
      initialSsoEnabled?: boolean
      initialPreset?: IdpPresetId
      initialEnforceDomain?: boolean
      className?: string
    }
    
    const idpPresets: IdpPreset[] = [
      {
        id: 'okta',
        name: 'Okta',
        subtitle: 'SAML 2.0 & OIDC Enterprise',
        badge: 'Popular',
        defaultSsoUrl: 'https://company.okta.com/app/sso/saml',
        defaultEntityId: 'http://www.okta.com/exk849201',
        domain: 'company.okta.com',
        defaultCert: `-----BEGIN CERTIFICATE-----
    MIIDpDCCAoygAwIBAgIGAX2v5L3oMA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJV
    UzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsG
    A1UECgwET2t0YTEUMBIGA1UECwwLU1NPIFNlcnZpY2UxGDAWBgNVBAMMD2NvbXBhbnkub2t0
    YS5jb20xIDAeBgkqhkiG9w0BCQEWEWFkbWluQG9rdGEuY29tMB4XDTI2MDgyMTE0MzAwMFoX
    DTI4MTAyMTE0MzAwMFowgZIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh
    -----END CERTIFICATE-----`,
      },
      {
        id: 'entra',
        name: 'Microsoft Entra ID',
        subtitle: 'Azure AD & Microsoft 365',
        badge: 'Enterprise',
        defaultSsoUrl: 'https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47/saml2',
        defaultEntityId: 'https://sts.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47/',
        domain: 'login.microsoftonline.com',
        defaultCert: `-----BEGIN CERTIFICATE-----
    MIIDBTCCAe2gAwIBAgIQJn0zJk1vVbtJpZ0R4k0D1DANBgkqhkiG9w0BAQsFADAtMSsw
    KQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTI2MDgy
    MTE0MzAwMFoXDTI4MTAyMTE0MzAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNz
    Y29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
    -----END CERTIFICATE-----`,
      },
      {
        id: 'google',
        name: 'Google Workspace',
        subtitle: 'Google Cloud Identity SAML',
        badge: 'SAML 2.0',
        defaultSsoUrl: 'https://accounts.google.com/o/saml2/idp?idpid=C0123abcd',
        defaultEntityId: 'https://accounts.google.com/o/saml2?idpid=C0123abcd',
        domain: 'accounts.google.com',
        defaultCert: `-----BEGIN CERTIFICATE-----
    MIIDdDCCAlygAwIBAgIGAY7GZpBqMA0GCSqGSIb3DQEBCwUAMHQxCzAJBgNVBAYTAlVT
    MRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MR8wHQYD
    VQQKExZHb29nbGUgV29ya3NwYWNlIElBTSExMB8GA1UEAxMYR29vZ2xlIElkUCBTYW1s
    IFNpZ25lcjAeFw0yNjA4MjExNDMwMDBaFw0yODEwMjExNDMwMDBaMHQxCzAJBgNVBAYT
    -----END CERTIFICATE-----`,
      },
      {
        id: 'custom',
        name: 'Custom SAML 2.0',
        subtitle: 'PingFederate, OneLogin, Keycloak',
        badge: 'Universal',
        defaultSsoUrl: 'https://sso.company.com/idp/profile/SAML2/POST/SSO',
        defaultEntityId: 'https://sso.company.com/idp/shibboleth',
        domain: 'sso.company.com',
        defaultCert: `-----BEGIN CERTIFICATE-----
    MIIDcDCCAligAwIBAgIJAN8FpW1mG+hBMA0GCSqGSIb3DQEBCwUAMFgxCzAJBgNVBAYT
    AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMQ8wDQYDVQQKDAZDdXN0b20xIDAeBgNVBAMM
    F2N1c3RvbS5pZHAucHJvdmlkZXIubGFiMB4XDTI2MDgyMTE0MzAwMFoXDTI4MTAyMTE0
    MzAwMFowWDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExDzANBgNVBAoM
    -----END CERTIFICATE-----`,
      },
    ]
    
    const spAcsUrl = 'https://auth.uipkge.dev/sso/saml/callback'
    const spEntityId = 'https://auth.uipkge.dev/sso/saml/metadata'
    const spMetadataXmlUrl = 'https://auth.uipkge.dev/sso/saml/metadata.xml'
    const spCertificatePem = `-----BEGIN CERTIFICATE-----
    MIIDODCCAiCgAwIBAgIUW0Qz0K7VzN8XlP4wQ6k9J1vYgLAwDQYJKoZIhvcNAQEL
    BQAwMzEXMBUGA1UEAwwNYXV0aC51aXBrZ2UuZGV2MRgwFgYDVQQKDA9VSVBLR0Ug
    QXV0aCBTU08wHhcNMjYwODIxMTQzMDAwWhcNMzEwODIxMTQzMDAwWjAzMRcwFQYD
    VQQDDA5hdXRoLnVpcGtnZS5kZXYxGDAWBgNVBAoMD1VJUEtHRSBBdXRoIFNTTzCC
    ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL1K9sU3FvX+7nQ6m2W4j1k9
    -----END CERTIFICATE-----`
    
    const simulatedClaims: ClaimAttribute[] = [
      { samlAttribute: 'urn:oid:0.9.2342.19200300.100.1.1', mappedField: 'nameID', sampleValue: '[email protected]' },
      {
        samlAttribute: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress',
        mappedField: 'email',
        sampleValue: '[email protected]',
      },
      {
        samlAttribute: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname',
        mappedField: 'firstName',
        sampleValue: 'Alex',
      },
      {
        samlAttribute: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname',
        mappedField: 'lastName',
        sampleValue: 'Morgan',
      },
      {
        samlAttribute: 'http://schemas.xmlsoap.org/claims/Group',
        mappedField: 'groups',
        sampleValue: '["Engineering", "IAM-Admins", "SSO-Users"]',
      },
      {
        samlAttribute: 'urn:oasis:names:tc:SAML:2.0:assertion:SessionIndex',
        mappedField: 'sessionIndex',
        sampleValue: '_sess_8f29ab0192e47',
      },
    ]
    
    export function SsoIdentityProviderConfig({
      initialSsoEnabled = true,
      initialPreset = 'okta',
      initialEnforceDomain = true,
      className,
    }: SsoIdentityProviderConfigProps) {
      const [ssoEnabled, setSsoEnabled] = React.useState(initialSsoEnabled)
      const [selectedPresetId, setSelectedPresetId] = React.useState<IdpPresetId>(initialPreset)
      const [enforceDomain, setEnforceDomain] = React.useState(initialEnforceDomain)
    
      const currentPreset = React.useMemo(() => {
        return idpPresets.find((p) => p.id === selectedPresetId) ?? idpPresets[0]
      }, [selectedPresetId])
    
      const [idpSsoUrl, setIdpSsoUrl] = React.useState(currentPreset.defaultSsoUrl)
      const [idpEntityId, setIdpEntityId] = React.useState(currentPreset.defaultEntityId)
      const [idpCertPem, setIdpCertPem] = React.useState(currentPreset.defaultCert)
    
      const [copiedKey, setCopiedKey] = React.useState<string | null>(null)
      const [isDraggingXml, setIsDraggingXml] = React.useState(false)
      const [xmlUploadedNotice, setXmlUploadedNotice] = React.useState(false)
      const [isSaving, setIsSaving] = React.useState(false)
      const [saveSuccessNotice, setSaveSuccessNotice] = React.useState(false)
      const [testModalOpen, setTestModalOpen] = React.useState(false)
    
      const copyTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)
    
      const copyText = React.useCallback((text: string, key: string) => {
        navigator.clipboard?.writeText(text)
        setCopiedKey(key)
        if (copyTimerRef.current) clearTimeout(copyTimerRef.current)
        copyTimerRef.current = setTimeout(() => {
          setCopiedKey((prev) => (prev === key ? null : prev))
        }, 2000)
      }, [])
    
      const handleSelectPreset = React.useCallback((presetId: IdpPresetId) => {
        setSelectedPresetId(presetId)
        const preset = idpPresets.find((p) => p.id === presetId)
        if (preset) {
          setIdpSsoUrl(preset.defaultSsoUrl)
          setIdpEntityId(preset.defaultEntityId)
          setIdpCertPem(preset.defaultCert)
          setXmlUploadedNotice(false)
        }
      }, [])
    
      const downloadSpCertificate = React.useCallback(() => {
        if (typeof window === 'undefined') return
        const blob = new Blob([spCertificatePem], { type: 'application/x-pem-file' })
        const url = URL.createObjectURL(blob)
        const anchor = document.createElement('a')
        anchor.href = url
        anchor.download = 'uipkge-sp-certificate.crt'
        document.body.appendChild(anchor)
        anchor.click()
        document.body.removeChild(anchor)
        URL.revokeObjectURL(url)
      }, [])
    
      const simulateXmlUpload = React.useCallback(() => {
        const preset = currentPreset
        setIdpSsoUrl(preset.defaultSsoUrl)
        setIdpEntityId(preset.defaultEntityId)
        setIdpCertPem(preset.defaultCert)
        setXmlUploadedNotice(true)
        setTimeout(() => {
          setXmlUploadedNotice(false)
        }, 4000)
      }, [currentPreset])
    
      const handleSaveConfig = React.useCallback(() => {
        if (isSaving) return
        setIsSaving(true)
        setTimeout(() => {
          setIsSaving(false)
          setSaveSuccessNotice(true)
          setTimeout(() => {
            setSaveSuccessNotice(false)
          }, 3500)
        }, 600)
      }, [isSaving])
    
      const openTestModal = React.useCallback(() => {
        setTestModalOpen(true)
      }, [])
    
      return (
        <div data-slot="sso-identity-provider-config" className={cn('w-full space-y-6', className)}>
          {/* Header Banner & Top Controls */}
          <Card className="border-border bg-card/80 shadow-xs backdrop-blur-xs">
            <CardHeader className="pb-4">
              <div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
                <div className="space-y-1">
                  <div className="flex flex-wrap items-center gap-2.5">
                    <div className="bg-primary/10 text-primary flex size-8 items-center justify-center rounded-lg">
                      <KeyRound className="size-4" />
                    </div>
                    <CardTitle className="text-base font-semibold">Single Sign-On (SSO) & SAML 2.0 Configuration</CardTitle>
                    {ssoEnabled ? (
                      <Badge variant="success" className="font-mono text-xs">
                        <span className="bg-success mr-1.5 inline-block size-2 animate-pulse rounded-full" />
                        SSO Active
                      </Badge>
                    ) : (
                      <Badge variant="outline" className="text-muted-foreground border-border font-mono text-xs">
                        Disabled
                      </Badge>
                    )}
                  </div>
                  <CardDescription className="text-xs">
                    Connect your enterprise IdP (Okta, Azure AD / Entra, Google Workspace, OneLogin) for federated SAML
                    authentication.
                  </CardDescription>
                </div>
    
                <div className="flex flex-wrap items-center gap-3">
                  <div className="border-border bg-muted/30 flex items-center gap-3 rounded-lg border px-3 py-1.5">
                    <div className="text-right">
                      <p className="text-foreground text-xs font-medium">SSO Status</p>
                      <p className="text-muted-foreground text-xs">
                        {ssoEnabled ? 'Enabled for @company.com' : 'Disabled'}
                      </p>
                    </div>
                    <Switch checked={ssoEnabled} onCheckedChange={setSsoEnabled} aria-label="Toggle Single Sign-On" />
                  </div>
    
                  <Button
                    variant="outline"
                    size="sm"
                    className="gap-1.5 text-xs font-medium"
                    disabled={!ssoEnabled}
                    onClick={openTestModal}
                  >
                    <Play className="size-3.5" />
                    <span>Test SAML Login</span>
                  </Button>
                </div>
              </div>
            </CardHeader>
          </Card>
    
          {/* Save Success Banner */}
          {saveSuccessNotice && (
            <div
              className="border-success/30 bg-success/10 text-success flex items-center justify-between rounded-lg border px-4 py-3 text-xs"
              role="status"
            >
              <div className="flex items-center gap-2">
                <CheckCircle2 className="size-4 shrink-0" />
                <span>
                  SSO configuration saved successfully. All assertions will validate against {currentPreset.name}.
                </span>
              </div>
              <button
                type="button"
                className="text-success cursor-pointer hover:opacity-75"
                aria-label="Dismiss alert"
                onClick={() => setSaveSuccessNotice(false)}
              >
                <X className="size-3.5" />
              </button>
            </div>
          )}
    
          {/* IdP Preset Picker Grid */}
          <div className="space-y-3">
            <div className="flex items-center justify-between">
              <div className="space-y-0.5">
                <h3 className="text-foreground text-xs font-semibold tracking-tight uppercase">
                  Identity Provider Presets
                </h3>
                <p className="text-muted-foreground text-xs">
                  Choose your identity platform to load SAML endpoints and formatting defaults
                </p>
              </div>
              <Badge variant="outline" className="border-border font-mono text-xs">
                SAML 2.0 / Web SSO
              </Badge>
            </div>
    
            <div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4">
              {idpPresets.map((preset) => {
                const isSelected = selectedPresetId === preset.id
                return (
                  <button
                    key={preset.id}
                    type="button"
                    className={cn(
                      'group relative flex flex-col justify-between rounded-xl border p-4 text-left transition-colors duration-150',
                      'focus-visible:ring-ring cursor-pointer focus-visible:ring-2 focus-visible:outline-none',
                      isSelected
                        ? 'border-primary bg-primary/5 ring-primary shadow-xs ring-1'
                        : 'border-border bg-card hover:border-primary/40 hover:bg-accent/40',
                    )}
                    onClick={() => handleSelectPreset(preset.id)}
                  >
                    <div className="space-y-2">
                      <div className="flex items-center justify-between">
                        <div
                          className={cn(
                            'flex size-8 items-center justify-center rounded-lg text-xs font-bold transition-colors',
                            isSelected
                              ? 'bg-primary text-primary-foreground'
                              : 'bg-muted text-foreground group-hover:bg-primary/10 group-hover:text-primary',
                          )}
                        >
                          {preset.id === 'okta' && <span>O</span>}
                          {preset.id === 'entra' && <span></span>}
                          {preset.id === 'google' && <span>G</span>}
                          {preset.id === 'custom' && <Globe className="size-4" />}
                        </div>
                        <Badge variant={isSelected ? 'default' : 'secondary'} className="font-mono text-xs">
                          {preset.badge}
                        </Badge>
                      </div>
    
                      <div>
                        <p className="text-foreground text-sm font-semibold">{preset.name}</p>
                        <p className="text-muted-foreground mt-0.5 text-xs">{preset.subtitle}</p>
                      </div>
                    </div>
    
                    <div className="border-border/60 mt-4 flex items-center justify-between border-t pt-2 text-xs">
                      <span className="text-muted-foreground max-w-[140px] truncate font-mono text-xs">
                        {preset.domain}
                      </span>
                      {isSelected ? (
                        <span className="text-primary flex items-center gap-1 text-xs font-medium">
                          <Check className="size-3" />
                          <span>Active</span>
                        </span>
                      ) : (
                        <span className="text-muted-foreground group-hover:text-foreground text-xs">Select →</span>
                      )}
                    </div>
                  </button>
                )
              })}
            </div>
          </div>
    
          {/* 2-Column Configuration Form */}
          <div className="grid grid-cols-1 gap-6 lg:grid-cols-12">
            {/* Left Column: Service Provider (SP) Credentials */}
            <div className="space-y-6 lg:col-span-6">
              <Card className="border-border bg-card shadow-xs">
                <CardHeader className="pb-4">
                  <div className="flex items-center justify-between">
                    <div className="flex items-center gap-2">
                      <div className="bg-primary/10 text-primary flex size-7 items-center justify-center rounded-md">
                        <ShieldCheck className="size-4" />
                      </div>
                      <div>
                        <CardTitle className="text-sm font-semibold">Service Provider (SP) Credentials</CardTitle>
                        <CardDescription className="text-xs">
                          Copy these values into your Identity Provider's SAML setup
                        </CardDescription>
                      </div>
                    </div>
                    <Badge variant="outline" className="font-mono text-xs">
                      Our Endpoints
                    </Badge>
                  </div>
                </CardHeader>
    
                <CardContent className="space-y-4 pt-0">
                  {/* ACS URL */}
                  <div className="space-y-1.5">
                    <div className="flex items-center justify-between">
                      <label htmlFor="sp-acs-url-react" className="text-foreground text-xs font-medium">
                        Assertion Consumer Service (ACS) URL
                      </label>
                      <Button
                        variant="ghost"
                        size="sm"
                        className="text-muted-foreground hover:text-foreground h-6 px-2 text-xs"
                        onClick={() => copyText(spAcsUrl, 'acs')}
                      >
                        {copiedKey === 'acs' ? (
                          <Check className="text-success mr-1 size-3" />
                        ) : (
                          <Copy className="mr-1 size-3" />
                        )}
                        <span>{copiedKey === 'acs' ? 'Copied' : 'Copy'}</span>
                      </Button>
                    </div>
                    <Input
                      id="sp-acs-url-react"
                      value={spAcsUrl}
                      readOnly
                      className="bg-muted/30 font-mono text-xs select-all"
                    />
                    <p className="text-muted-foreground text-xs">
                      The callback URL where your IdP transmits HTTP-POST SAML assertion responses.
                    </p>
                  </div>
    
                  {/* Entity ID / Audience */}
                  <div className="space-y-1.5">
                    <div className="flex items-center justify-between">
                      <label htmlFor="sp-entity-id-react" className="text-foreground text-xs font-medium">
                        SP Entity ID / Audience URI
                      </label>
                      <Button
                        variant="ghost"
                        size="sm"
                        className="text-muted-foreground hover:text-foreground h-6 px-2 text-xs"
                        onClick={() => copyText(spEntityId, 'entity-id')}
                      >
                        {copiedKey === 'entity-id' ? (
                          <Check className="text-success mr-1 size-3" />
                        ) : (
                          <Copy className="mr-1 size-3" />
                        )}
                        <span>{copiedKey === 'entity-id' ? 'Copied' : 'Copy'}</span>
                      </Button>
                    </div>
                    <Input
                      id="sp-entity-id-react"
                      value={spEntityId}
                      readOnly
                      className="bg-muted/30 font-mono text-xs select-all"
                    />
                    <p className="text-muted-foreground text-xs">
                      The unique audience restriction URI identifying our service to your IdP.
                    </p>
                  </div>
    
                  {/* Protocol Specifications Summary */}
                  <div className="border-border bg-muted/20 space-y-2 rounded-lg border p-3">
                    <p className="text-foreground text-xs font-medium">Protocol & Binding Requirements</p>
                    <div className="grid grid-cols-2 gap-2 text-xs">
                      <div>
                        <span className="text-muted-foreground block text-xs">NameID Format</span>
                        <span className="text-foreground font-mono text-xs font-medium">EmailAddress (urn:oasis:...)</span>
                      </div>
                      <div>
                        <span className="text-muted-foreground block text-xs">SAML Binding</span>
                        <span className="text-foreground font-mono text-xs font-medium">HTTP-POST</span>
                      </div>
                      <div>
                        <span className="text-muted-foreground block text-xs">Signing Algorithm</span>
                        <span className="text-foreground font-mono text-xs font-medium">RSA-SHA256</span>
                      </div>
                      <div>
                        <span className="text-muted-foreground block text-xs">Assertion Encryption</span>
                        <span className="text-foreground font-mono text-xs font-medium">Optional (AES-256-GCM)</span>
                      </div>
                    </div>
                  </div>
    
                  <Separator />
    
                  {/* SP Public Certificate */}
                  <div className="space-y-2">
                    <div className="flex items-center justify-between">
                      <label className="text-foreground text-xs font-medium">SP X.509 Public Certificate</label>
                      <div className="flex items-center gap-1.5">
                        <Button
                          variant="ghost"
                          size="sm"
                          className="text-muted-foreground hover:text-foreground h-6 px-2 text-xs"
                          onClick={() => copyText(spCertificatePem, 'sp-cert')}
                        >
                          {copiedKey === 'sp-cert' ? (
                            <Check className="text-success mr-1 size-3" />
                          ) : (
                            <Copy className="mr-1 size-3" />
                          )}
                          <span>{copiedKey === 'sp-cert' ? 'Copied' : 'Copy PEM'}</span>
                        </Button>
                        <Button
                          aria-label="Download attachment"
                          variant="outline"
                          size="sm"
                          className="h-6 gap-1 px-2 text-xs"
                          onClick={downloadSpCertificate}
                        >
                          <Download className="size-3" />
                          <span>Download .crt</span>
                        </Button>
                      </div>
                    </div>
    
                    <pre className="border-border bg-muted/40 text-foreground max-h-[140px] overflow-x-auto rounded-md border p-3 font-mono text-xs leading-relaxed select-all">
                      <code>{spCertificatePem}</code>
                    </pre>
                    <p className="text-muted-foreground text-xs">
                      Import this certificate into your IdP if assertion signing or encryption is required.
                    </p>
                  </div>
                </CardContent>
    
                <CardFooter className="border-border text-muted-foreground flex items-center justify-between border-t pt-3 text-xs">
                  <span className="flex items-center gap-1.5">
                    <FileCode className="size-3.5" />
                    <span>SP Metadata XML Endpoint</span>
                  </span>
                  <Button
                    variant="ghost"
                    size="sm"
                    className="text-primary hover:text-primary/80 h-6 gap-1 px-2 text-xs"
                    onClick={() => copyText(spMetadataXmlUrl, 'meta-url')}
                  >
                    <span>{copiedKey === 'meta-url' ? 'Copied URL' : 'Copy metadata.xml URL'}</span>
                    <ExternalLink className="size-3" />
                  </Button>
                </CardFooter>
              </Card>
            </div>
    
            {/* Right Column: Identity Provider (IdP) Metadata Form */}
            <div className="space-y-6 lg:col-span-6">
              <Card className="border-border bg-card shadow-xs">
                <CardHeader className="pb-4">
                  <div className="flex items-center justify-between">
                    <div className="flex items-center gap-2">
                      <div className="bg-primary/10 text-primary flex size-7 items-center justify-center rounded-md">
                        <Lock className="size-4" />
                      </div>
                      <div>
                        <CardTitle className="text-sm font-semibold">Identity Provider (IdP) Metadata</CardTitle>
                        <CardDescription className="text-xs">
                          Configure SAML endpoints and credentials provided by {currentPreset.name}
                        </CardDescription>
                      </div>
                    </div>
                    <Badge variant="secondary" className="font-mono text-xs">
                      {currentPreset.name}
                    </Badge>
                  </div>
                </CardHeader>
    
                <CardContent className="space-y-4 pt-0">
                  {/* XML Upload Dropzone */}
                  <div
                    className={cn(
                      'border-border relative flex cursor-pointer flex-col items-center justify-center rounded-xl border border-dashed p-4 text-center transition-colors',
                      isDraggingXml
                        ? 'border-primary bg-primary/10'
                        : 'bg-muted/20 hover:border-primary/50 hover:bg-muted/40',
                    )}
                    onDragOver={(e) => {
                      e.preventDefault()
                      setIsDraggingXml(true)
                    }}
                    onDragLeave={() => setIsDraggingXml(false)}
                    onDrop={(e) => {
                      e.preventDefault()
                      setIsDraggingXml(false)
                      simulateXmlUpload()
                    }}
                    onClick={simulateXmlUpload}
                  >
                    <div className="bg-primary/10 text-primary mb-2 grid size-9 place-items-center rounded-full">
                      <UploadCloud className="size-4" />
                    </div>
                    <p className="text-foreground text-xs font-semibold">
                      Drag & drop IdP <span className="font-mono">metadata.xml</span> to auto-fill
                    </p>
                    <p className="text-muted-foreground mt-0.5 text-xs">
                      Or click here to simulate parsing your Identity Provider metadata file
                    </p>
                  </div>
    
                  {/* XML Notice */}
                  {xmlUploadedNotice && (
                    <div className="border-success/30 bg-success/10 text-success flex items-center gap-2 rounded-lg border p-2.5 text-xs">
                      <CheckCircle2 className="size-4 shrink-0" />
                      <span>Parsed SSO URL, Entity ID, and X.509 Certificate from metadata XML.</span>
                    </div>
                  )}
    
                  {/* IdP SSO URL */}
                  <div className="space-y-1.5">
                    <label htmlFor="idp-sso-url-react" className="text-foreground text-xs font-medium">
                      IdP Single Sign-On (SSO) URL
                    </label>
                    <Input
                      id="idp-sso-url-react"
                      value={idpSsoUrl}
                      onChange={(e) => setIdpSsoUrl(e.target.value)}
                      placeholder="https://company.okta.com/app/sso/saml"
                      className="font-mono text-xs"
                    />
                    <p className="text-muted-foreground text-xs">
                      The HTTP-POST endpoint at your IdP where users are redirected for authentication.
                    </p>
                  </div>
    
                  {/* IdP Entity ID / Issuer */}
                  <div className="space-y-1.5">
                    <label htmlFor="idp-entity-id-react" className="text-foreground text-xs font-medium">
                      IdP Entity ID / Issuer URI
                    </label>
                    <Input
                      id="idp-entity-id-react"
                      value={idpEntityId}
                      onChange={(e) => setIdpEntityId(e.target.value)}
                      placeholder="http://www.okta.com/exk849201"
                      className="font-mono text-xs"
                    />
                    <p className="text-muted-foreground text-xs">
                      The unique issuer string provided in your IdP metadata document.
                    </p>
                  </div>
    
                  {/* X.509 Certificate PEM Textarea */}
                  <div className="space-y-1.5">
                    <div className="flex items-center justify-between">
                      <label htmlFor="idp-cert-pem-react" className="text-foreground text-xs font-medium">
                        IdP X.509 Signing Certificate (PEM)
                      </label>
                      <Badge variant="outline" className="border-success/40 text-success font-mono text-xs">
                        Valid (Expires Oct 2028)
                      </Badge>
                    </div>
                    <Textarea
                      id="idp-cert-pem-react"
                      value={idpCertPem}
                      onValueChange={setIdpCertPem}
                      rows={5}
                      noResize
                      className="border-border/70 font-mono text-xs leading-relaxed"
                      placeholder="-----BEGIN CERTIFICATE-----..."
                    />
                    <p className="text-muted-foreground text-xs">
                      Public X.509 certificate used by our SP to verify the cryptographic signature on SAML assertions.
                    </p>
                  </div>
    
                  <Separator />
    
                  {/* Domain Enforcement Switch */}
                  <div className="border-border bg-muted/20 flex items-start justify-between gap-3 rounded-lg border p-3">
                    <div className="space-y-0.5">
                      <div className="flex items-center gap-1.5">
                        <ShieldAlert className="text-primary size-4" />
                        <p className="text-foreground text-xs font-semibold">Enforce SSO for all domain emails</p>
                      </div>
                      <p className="text-muted-foreground text-xs">
                        Require all users with <span className="text-foreground font-mono font-medium">@company.com</span>{' '}
                        email addresses to log in via SSO. Password logins will be blocked.
                      </p>
                    </div>
                    <Switch
                      checked={enforceDomain}
                      onCheckedChange={setEnforceDomain}
                      className="mt-1"
                      aria-label="Enforce SSO for domain"
                    />
                  </div>
                </CardContent>
    
                <CardFooter className="border-border flex flex-wrap items-center justify-between gap-2 border-t pt-4">
                  <Button
                    variant="ghost"
                    size="sm"
                    className="text-muted-foreground hover:text-foreground text-xs"
                    onClick={() => handleSelectPreset(selectedPresetId)}
                  >
                    Reset to Defaults
                  </Button>
                  <Button
                    size="sm"
                    className="gap-1.5 text-xs font-medium shadow-xs"
                    disabled={isSaving || !idpSsoUrl.trim() || !idpEntityId.trim()}
                    onClick={handleSaveConfig}
                  >
                    {isSaving ? <RotateCw className="size-3.5 animate-spin" /> : <Save className="size-3.5" />}
                    <span>{isSaving ? 'Saving...' : 'Save SSO Configuration'}</span>
                  </Button>
                </CardFooter>
              </Card>
            </div>
          </div>
    
          {/* Test SAML Login Diagnostic Dialog */}
          <Dialog open={testModalOpen} onOpenChange={setTestModalOpen}>
            <DialogContent className="sm:max-w-xl">
              <DialogHeader>
                <div className="flex items-center gap-2">
                  <div className="bg-primary/10 text-primary flex size-8 items-center justify-center rounded-lg">
                    <UserCheck className="size-4" />
                  </div>
                  <div>
                    <DialogTitle>SAML 2.0 Authentication Diagnostic</DialogTitle>
                    <DialogDescription>Live assertion handshake simulation with {currentPreset.name}</DialogDescription>
                  </div>
                </div>
              </DialogHeader>
    
              <div className="space-y-4 py-2">
                {/* Handshake Status Banner */}
                <div className="border-success/30 bg-success/10 text-success flex items-center justify-between rounded-lg border p-3 text-xs">
                  <div className="flex items-center gap-2">
                    <CheckCircle2 className="size-4 shrink-0" />
                    <div>
                      <p className="font-semibold">HTTP 200 OK — SAML Handshake Validated</p>
                      <p className="text-xs opacity-90">All signature envelopes matched X.509 certificate</p>
                    </div>
                  </div>
                  <Badge variant="success" className="font-mono text-xs">
                    Success
                  </Badge>
                </div>
    
                {/* Step Verification Checklist */}
                <div className="border-border bg-muted/20 space-y-2 rounded-lg border p-3 text-xs">
                  <h4 className="text-foreground font-semibold">Verification Pipeline</h4>
                  <div className="space-y-1.5">
                    <div className="text-foreground flex items-center gap-2">
                      <Check className="text-success size-3.5 shrink-0" />
                      <span>
                        1. AuthnRequest dispatched with SP signature to{' '}
                        <code className="font-mono text-xs">{idpSsoUrl}</code>
                      </span>
                    </div>
                    <div className="text-foreground flex items-center gap-2">
                      <Check className="text-success size-3.5 shrink-0" />
                      <span>2. SAMLResponse assertion received and verified with RSA-SHA256 signature</span>
                    </div>
                    <div className="text-foreground flex items-center gap-2">
                      <Check className="text-success size-3.5 shrink-0" />
                      <span>
                        3. AudienceRestriction matched <code className="font-mono text-xs">{spEntityId}</code>
                      </span>
                    </div>
                    <div className="text-foreground flex items-center gap-2">
                      <Check className="text-success size-3.5 shrink-0" />
                      <span>4. Assertion timestamp valid within 300s clock-skew tolerance window</span>
                    </div>
                  </div>
                </div>
    
                {/* Asserted Claims Table */}
                <div className="space-y-1.5">
                  <h4 className="text-foreground text-xs font-semibold">Asserted Identity Claims & Attributes</h4>
                  <div className="border-border bg-muted/10 overflow-hidden rounded-md border text-xs">
                    <table className="w-full">
                      <thead>
                        <tr className="border-border bg-muted/40 text-muted-foreground border-b text-left font-medium">
                          <th className="p-2 text-xs">Field</th>
                          <th className="p-2 text-xs">Sample Claim Value</th>
                        </tr>
                      </thead>
                      <tbody className="divide-border divide-y font-mono text-xs">
                        {simulatedClaims.map((claim) => (
                          <tr key={claim.mappedField} className="hover:bg-muted/20">
                            <td className="text-primary p-2 font-semibold">{claim.mappedField}</td>
                            <td className="text-foreground max-w-[240px] truncate p-2">{claim.sampleValue}</td>
                          </tr>
                        ))}
                      </tbody>
                    </table>
                  </div>
                </div>
              </div>
    
              <DialogFooter>
                <Button variant="outline" size="sm" onClick={() => setTestModalOpen(false)}>
                  Close
                </Button>
                <Button size="sm" className="gap-1.5" onClick={openTestModal}>
                  <RotateCw className="size-3.5" />
                  <span>Re-test Handshake</span>
                </Button>
              </DialogFooter>
            </DialogContent>
          </Dialog>
        </div>
      )
    }
    
    export default SsoIdentityProviderConfig
    

Raw manifest:https://uipkge.dev/r/react/sso-identity-provider-config.json