{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "sso-identity-provider-config",
  "title": "Sso Identity Provider Config",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-vue/blocks/sso-identity-provider-config/SsoIdentityProviderConfig.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport type { HTMLAttributes } from 'vue'\nimport {\n  Check,\n  CheckCircle2,\n  Copy,\n  Download,\n  ExternalLink,\n  FileCode,\n  Globe,\n  KeyRound,\n  Lock,\n  Play,\n  RotateCw,\n  Save,\n  ShieldAlert,\n  ShieldCheck,\n  UploadCloud,\n  UserCheck,\n  X,\n} from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n} from '@/components/ui/dialog'\nimport { Input } from '@/components/ui/input'\nimport { Separator } from '@/components/ui/separator'\nimport { Switch } from '@/components/ui/switch'\nimport { Textarea } from '@/components/ui/textarea'\n\nexport type IdpPresetId = 'okta' | 'entra' | 'google' | 'custom'\n\ninterface IdpPreset {\n  id: IdpPresetId\n  name: string\n  subtitle: string\n  badge: string\n  defaultSsoUrl: string\n  defaultEntityId: string\n  domain: string\n  defaultCert: string\n}\n\ninterface ClaimAttribute {\n  samlAttribute: string\n  mappedField: string\n  sampleValue: string\n}\n\ninterface Props {\n  initialSsoEnabled?: boolean\n  initialPreset?: IdpPresetId\n  initialEnforceDomain?: boolean\n  class?: HTMLAttributes['class']\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n  initialSsoEnabled: true,\n  initialPreset: 'okta',\n  initialEnforceDomain: true,\n})\n\nconst idpPresets: IdpPreset[] = [\n  {\n    id: 'okta',\n    name: 'Okta',\n    subtitle: 'SAML 2.0 & OIDC Enterprise',\n    badge: 'Popular',\n    defaultSsoUrl: 'https://company.okta.com/app/sso/saml',\n    defaultEntityId: 'http://www.okta.com/exk849201',\n    domain: 'company.okta.com',\n    defaultCert: `-----BEGIN CERTIFICATE-----\nMIIDpDCCAoygAwIBAgIGAX2v5L3oMA0GCSqGSIb3DQEBCwUAMIGSMQswCQYDVQQGEwJV\nUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsG\nA1UECgwET2t0YTEUMBIGA1UECwwLU1NPIFNlcnZpY2UxGDAWBgNVBAMMD2NvbXBhbnkub2t0\nYS5jb20xIDAeBgkqhkiG9w0BCQEWEWFkbWluQG9rdGEuY29tMB4XDTI2MDgyMTE0MzAwMFoX\nDTI4MTAyMTE0MzAwMFowgZIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh\n-----END CERTIFICATE-----`,\n  },\n  {\n    id: 'entra',\n    name: 'Microsoft Entra ID',\n    subtitle: 'Azure AD & Microsoft 365',\n    badge: 'Enterprise',\n    defaultSsoUrl: 'https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47/saml2',\n    defaultEntityId: 'https://sts.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47/',\n    domain: 'login.microsoftonline.com',\n    defaultCert: `-----BEGIN CERTIFICATE-----\nMIIDBTCCAe2gAwIBAgIQJn0zJk1vVbtJpZ0R4k0D1DANBgkqhkiG9w0BAQsFADAtMSsw\nKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTI2MDgy\nMTE0MzAwMFoXDTI4MTAyMTE0MzAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNz\nY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\n-----END CERTIFICATE-----`,\n  },\n  {\n    id: 'google',\n    name: 'Google Workspace',\n    subtitle: 'Google Cloud Identity SAML',\n    badge: 'SAML 2.0',\n    defaultSsoUrl: 'https://accounts.google.com/o/saml2/idp?idpid=C0123abcd',\n    defaultEntityId: 'https://accounts.google.com/o/saml2?idpid=C0123abcd',\n    domain: 'accounts.google.com',\n    defaultCert: `-----BEGIN CERTIFICATE-----\nMIIDdDCCAlygAwIBAgIGAY7GZpBqMA0GCSqGSIb3DQEBCwUAMHQxCzAJBgNVBAYTAlVT\nMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MR8wHQYD\nVQQKExZHb29nbGUgV29ya3NwYWNlIElBTSExMB8GA1UEAxMYR29vZ2xlIElkUCBTYW1s\nIFNpZ25lcjAeFw0yNjA4MjExNDMwMDBaFw0yODEwMjExNDMwMDBaMHQxCzAJBgNVBAYT\n-----END CERTIFICATE-----`,\n  },\n  {\n    id: 'custom',\n    name: 'Custom SAML 2.0',\n    subtitle: 'PingFederate, OneLogin, Keycloak',\n    badge: 'Universal',\n    defaultSsoUrl: 'https://sso.company.com/idp/profile/SAML2/POST/SSO',\n    defaultEntityId: 'https://sso.company.com/idp/shibboleth',\n    domain: 'sso.company.com',\n    defaultCert: `-----BEGIN CERTIFICATE-----\nMIIDcDCCAligAwIBAgIJAN8FpW1mG+hBMA0GCSqGSIb3DQEBCwUAMFgxCzAJBgNVBAYT\nAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMQ8wDQYDVQQKDAZDdXN0b20xIDAeBgNVBAMM\nF2N1c3RvbS5pZHAucHJvdmlkZXIubGFiMB4XDTI2MDgyMTE0MzAwMFoXDTI4MTAyMTE0\nMzAwMFowWDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExDzANBgNVBAoM\n-----END CERTIFICATE-----`,\n  },\n]\n\nconst spAcsUrl = 'https://auth.uipkge.dev/sso/saml/callback'\nconst spEntityId = 'https://auth.uipkge.dev/sso/saml/metadata'\nconst spNameIdFormat = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'\nconst spMetadataXmlUrl = 'https://auth.uipkge.dev/sso/saml/metadata.xml'\nconst spCertificatePem = `-----BEGIN CERTIFICATE-----\nMIIDODCCAiCgAwIBAgIUW0Qz0K7VzN8XlP4wQ6k9J1vYgLAwDQYJKoZIhvcNAQEL\nBQAwMzEXMBUGA1UEAwwNYXV0aC51aXBrZ2UuZGV2MRgwFgYDVQQKDA9VSVBLR0Ug\nQXV0aCBTU08wHhcNMjYwODIxMTQzMDAwWhcNMzEwODIxMTQzMDAwWjAzMRcwFQYD\nVQQDDA5hdXRoLnVpcGtnZS5kZXYxGDAWBgNVBAoMD1VJUEtHRSBBdXRoIFNTTzCC\nASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL1K9sU3FvX+7nQ6m2W4j1k9\n-----END CERTIFICATE-----`\n\nconst ssoEnabled = ref(props.initialSsoEnabled)\nconst selectedPresetId = ref<IdpPresetId>(props.initialPreset)\nconst enforceDomain = ref(props.initialEnforceDomain)\n\nconst currentPreset = computed(() => {\n  return idpPresets.find((p) => p.id === selectedPresetId.value) ?? idpPresets[0]\n})\n\nconst idpSsoUrl = ref(currentPreset.value.defaultSsoUrl)\nconst idpEntityId = ref(currentPreset.value.defaultEntityId)\nconst idpCertPem = ref(currentPreset.value.defaultCert)\n\nconst copiedKey = ref<string | null>(null)\nlet copyTimer: ReturnType<typeof setTimeout> | undefined\n\nasync function copyText(text: string, key: string) {\n  try {\n    await navigator.clipboard.writeText(text)\n    copiedKey.value = key\n    if (copyTimer) clearTimeout(copyTimer)\n    copyTimer = setTimeout(() => {\n      if (copiedKey.value === key) copiedKey.value = null\n    }, 2000)\n  } catch {\n    // Clipboard unavailable\n  }\n}\n\nfunction handleSelectPreset(presetId: IdpPresetId) {\n  selectedPresetId.value = presetId\n  const preset = idpPresets.find((p) => p.id === presetId)\n  if (preset) {\n    idpSsoUrl.value = preset.defaultSsoUrl\n    idpEntityId.value = preset.defaultEntityId\n    idpCertPem.value = preset.defaultCert\n    xmlUploadedNotice.value = false\n  }\n}\n\nfunction downloadSpCertificate() {\n  if (typeof window === 'undefined') return\n  const blob = new Blob([spCertificatePem], { type: 'application/x-pem-file' })\n  const url = URL.createObjectURL(blob)\n  const anchor = document.createElement('a')\n  anchor.href = url\n  anchor.download = 'uipkge-sp-certificate.crt'\n  document.body.appendChild(anchor)\n  anchor.click()\n  document.body.removeChild(anchor)\n  URL.revokeObjectURL(url)\n}\n\nconst isDraggingXml = ref(false)\nconst xmlUploadedNotice = ref(false)\n\nfunction handleXmlDrop(e: DragEvent) {\n  e.preventDefault()\n  isDraggingXml.value = false\n  simulateXmlUpload()\n}\n\nfunction handleXmlDragOver(e: DragEvent) {\n  e.preventDefault()\n  isDraggingXml.value = true\n}\n\nfunction handleXmlDragLeave() {\n  isDraggingXml.value = false\n}\n\nfunction simulateXmlUpload() {\n  const preset = currentPreset.value\n  idpSsoUrl.value = preset.defaultSsoUrl\n  idpEntityId.value = preset.defaultEntityId\n  idpCertPem.value = preset.defaultCert\n  xmlUploadedNotice.value = true\n  setTimeout(() => {\n    xmlUploadedNotice.value = false\n  }, 4000)\n}\n\nconst isSaving = ref(false)\nconst saveSuccessNotice = ref(false)\n\nfunction handleSaveConfig() {\n  if (isSaving.value) return\n  isSaving.value = true\n  setTimeout(() => {\n    isSaving.value = false\n    saveSuccessNotice.value = true\n    setTimeout(() => {\n      saveSuccessNotice.value = false\n    }, 3500)\n  }, 600)\n}\n\nconst testModalOpen = ref(false)\nconst isTesting = ref(false)\n\nfunction openTestModal() {\n  testModalOpen.value = true\n  isTesting.value = true\n  setTimeout(() => {\n    isTesting.value = false\n  }, 500)\n}\n\nconst simulatedClaims: ClaimAttribute[] = [\n  { samlAttribute: 'urn:oid:0.9.2342.19200300.100.1.1', mappedField: 'nameID', sampleValue: 'alex.morgan@company.com' },\n  {\n    samlAttribute: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress',\n    mappedField: 'email',\n    sampleValue: 'alex.morgan@company.com',\n  },\n  {\n    samlAttribute: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname',\n    mappedField: 'firstName',\n    sampleValue: 'Alex',\n  },\n  {\n    samlAttribute: 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname',\n    mappedField: 'lastName',\n    sampleValue: 'Morgan',\n  },\n  {\n    samlAttribute: 'http://schemas.xmlsoap.org/claims/Group',\n    mappedField: 'groups',\n    sampleValue: '[\"Engineering\", \"IAM-Admins\", \"SSO-Users\"]',\n  },\n  {\n    samlAttribute: 'urn:oasis:names:tc:SAML:2.0:assertion:SessionIndex',\n    mappedField: 'sessionIndex',\n    sampleValue: '_sess_8f29ab0192e47',\n  },\n]\n</script>\n\n<template>\n  <div data-slot=\"sso-identity-provider-config\" :class=\"cn('w-full space-y-6', props.class)\">\n    <!-- Header Banner & Top Controls -->\n    <Card class=\"border-border bg-card/80 shadow-xs backdrop-blur-xs\">\n      <CardHeader class=\"pb-4\">\n        <div class=\"flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between\">\n          <div class=\"space-y-1\">\n            <div class=\"flex flex-wrap items-center gap-2.5\">\n              <div class=\"bg-primary/10 text-primary flex size-8 items-center justify-center rounded-lg\">\n                <KeyRound class=\"size-4\" />\n              </div>\n              <CardTitle class=\"text-base font-semibold\">Single Sign-On (SSO) & SAML 2.0 Configuration</CardTitle>\n              <Badge v-if=\"ssoEnabled\" variant=\"success\" class=\"font-mono text-xs\">\n                <span class=\"bg-success mr-1.5 inline-block size-2 animate-pulse rounded-full\" />\n                SSO Active\n              </Badge>\n              <Badge v-else variant=\"outline\" class=\"text-muted-foreground border-border font-mono text-xs\">\n                Disabled\n              </Badge>\n            </div>\n            <CardDescription class=\"text-xs\">\n              Connect your enterprise IdP (Okta, Azure AD / Entra, Google Workspace, OneLogin) for federated SAML\n              authentication.\n            </CardDescription>\n          </div>\n\n          <div class=\"flex flex-wrap items-center gap-3\">\n            <div class=\"border-border bg-muted/30 flex items-center gap-3 rounded-lg border px-3 py-1.5\">\n              <div class=\"text-right\">\n                <p class=\"text-foreground text-xs font-medium\">SSO Status</p>\n                <p class=\"text-muted-foreground text-xs\">\n                  {{ ssoEnabled ? 'Enabled for @company.com' : 'Disabled' }}\n                </p>\n              </div>\n              <Switch v-model=\"ssoEnabled\" aria-label=\"Toggle Single Sign-On\" />\n            </div>\n\n            <Button\n              variant=\"outline\"\n              size=\"sm\"\n              class=\"gap-1.5 text-xs font-medium\"\n              :disabled=\"!ssoEnabled\"\n              @click=\"openTestModal\"\n            >\n              <Play class=\"size-3.5\" />\n              <span>Test SAML Login</span>\n            </Button>\n          </div>\n        </div>\n      </CardHeader>\n    </Card>\n\n    <!-- Save Success Banner -->\n    <div\n      v-if=\"saveSuccessNotice\"\n      class=\"border-success/30 bg-success/10 text-success flex items-center justify-between rounded-lg border px-4 py-3 text-xs\"\n      role=\"status\"\n    >\n      <div class=\"flex items-center gap-2\">\n        <CheckCircle2 class=\"size-4 shrink-0\" />\n        <span\n          >SSO configuration saved successfully. All assertions will validate against {{ currentPreset.name }}.</span\n        >\n      </div>\n      <button\n        type=\"button\"\n        class=\"text-success hover:opacity-75\"\n        aria-label=\"Dismiss alert\"\n        @click=\"saveSuccessNotice = false\"\n      >\n        <X class=\"size-3.5\" />\n      </button>\n    </div>\n\n    <!-- IdP Preset Picker Grid -->\n    <div class=\"space-y-3\">\n      <div class=\"flex items-center justify-between\">\n        <div class=\"space-y-0.5\">\n          <h3 class=\"text-foreground text-xs font-semibold tracking-tight uppercase\">Identity Provider Presets</h3>\n          <p class=\"text-muted-foreground text-xs\">\n            Choose your identity platform to load SAML endpoints and formatting defaults\n          </p>\n        </div>\n        <Badge variant=\"outline\" class=\"border-border font-mono text-xs\"> SAML 2.0 / Web SSO </Badge>\n      </div>\n\n      <div class=\"grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4\">\n        <button\n          v-for=\"preset in idpPresets\"\n          :key=\"preset.id\"\n          type=\"button\"\n          :class=\"\n            cn(\n              'group relative flex flex-col justify-between rounded-xl border p-4 text-left transition-colors duration-150',\n              'focus-visible:ring-ring cursor-pointer focus-visible:ring-2 focus-visible:outline-none',\n              selectedPresetId === preset.id\n                ? 'border-primary bg-primary/5 ring-primary shadow-xs ring-1'\n                : 'border-border bg-card hover:border-primary/40 hover:bg-accent/40',\n            )\n          \"\n          @click=\"handleSelectPreset(preset.id)\"\n        >\n          <div class=\"space-y-2\">\n            <div class=\"flex items-center justify-between\">\n              <div\n                :class=\"\n                  cn(\n                    'flex size-8 items-center justify-center rounded-lg text-xs font-bold transition-colors',\n                    selectedPresetId === preset.id\n                      ? 'bg-primary text-primary-foreground'\n                      : 'bg-muted text-foreground group-hover:bg-primary/10 group-hover:text-primary',\n                  )\n                \"\n              >\n                <span v-if=\"preset.id === 'okta'\">O</span>\n                <span v-else-if=\"preset.id === 'entra'\">⊞</span>\n                <span v-else-if=\"preset.id === 'google'\">G</span>\n                <Globe v-else class=\"size-4\" />\n              </div>\n              <Badge :variant=\"selectedPresetId === preset.id ? 'default' : 'secondary'\" class=\"font-mono text-xs\">\n                {{ preset.badge }}\n              </Badge>\n            </div>\n\n            <div>\n              <p class=\"text-foreground text-sm font-semibold\">{{ preset.name }}</p>\n              <p class=\"text-muted-foreground mt-0.5 text-xs\">{{ preset.subtitle }}</p>\n            </div>\n          </div>\n\n          <div class=\"border-border/60 mt-4 flex items-center justify-between border-t pt-2 text-xs\">\n            <span class=\"text-muted-foreground max-w-[140px] truncate font-mono text-xs\">{{ preset.domain }}</span>\n            <span\n              v-if=\"selectedPresetId === preset.id\"\n              class=\"text-primary flex items-center gap-1 text-xs font-medium\"\n            >\n              <Check class=\"size-3\" />\n              <span>Active</span>\n            </span>\n            <span v-else class=\"text-muted-foreground group-hover:text-foreground text-xs\">Select →</span>\n          </div>\n        </button>\n      </div>\n    </div>\n\n    <!-- 2-Column Configuration Form -->\n    <div class=\"grid grid-cols-1 gap-6 lg:grid-cols-12\">\n      <!-- Left Column: Service Provider (SP) Credentials -->\n      <div class=\"space-y-6 lg:col-span-6\">\n        <Card class=\"border-border bg-card shadow-xs\">\n          <CardHeader class=\"pb-4\">\n            <div class=\"flex items-center justify-between\">\n              <div class=\"flex items-center gap-2\">\n                <div class=\"bg-primary/10 text-primary flex size-7 items-center justify-center rounded-md\">\n                  <ShieldCheck class=\"size-4\" />\n                </div>\n                <div>\n                  <CardTitle class=\"text-sm font-semibold\">Service Provider (SP) Credentials</CardTitle>\n                  <CardDescription class=\"text-xs\">\n                    Copy these values into your Identity Provider's SAML setup\n                  </CardDescription>\n                </div>\n              </div>\n              <Badge variant=\"outline\" class=\"font-mono text-xs\">Our Endpoints</Badge>\n            </div>\n          </CardHeader>\n\n          <CardContent class=\"space-y-4 pt-0\">\n            <!-- ACS URL -->\n            <div class=\"space-y-1.5\">\n              <div class=\"flex items-center justify-between\">\n                <label for=\"sp-acs-url\" class=\"text-foreground text-xs font-medium\">\n                  Assertion Consumer Service (ACS) URL\n                </label>\n                <Button\n                  variant=\"ghost\"\n                  size=\"sm\"\n                  class=\"text-muted-foreground hover:text-foreground h-6 px-2 text-xs\"\n                  @click=\"copyText(spAcsUrl, 'acs')\"\n                >\n                  <Check v-if=\"copiedKey === 'acs'\" class=\"text-success mr-1 size-3\" />\n                  <Copy v-else class=\"mr-1 size-3\" />\n                  <span>{{ copiedKey === 'acs' ? 'Copied' : 'Copy' }}</span>\n                </Button>\n              </div>\n              <Input\n                id=\"sp-acs-url\"\n                :model-value=\"spAcsUrl\"\n                readonly\n                class=\"bg-muted/30 font-mono text-xs select-all\"\n              />\n              <p class=\"text-muted-foreground text-xs\">\n                The callback URL where your IdP transmits HTTP-POST SAML assertion responses.\n              </p>\n            </div>\n\n            <!-- Entity ID / Audience -->\n            <div class=\"space-y-1.5\">\n              <div class=\"flex items-center justify-between\">\n                <label for=\"sp-entity-id\" class=\"text-foreground text-xs font-medium\">\n                  SP Entity ID / Audience URI\n                </label>\n                <Button\n                  variant=\"ghost\"\n                  size=\"sm\"\n                  class=\"text-muted-foreground hover:text-foreground h-6 px-2 text-xs\"\n                  @click=\"copyText(spEntityId, 'entity-id')\"\n                >\n                  <Check v-if=\"copiedKey === 'entity-id'\" class=\"text-success mr-1 size-3\" />\n                  <Copy v-else class=\"mr-1 size-3\" />\n                  <span>{{ copiedKey === 'entity-id' ? 'Copied' : 'Copy' }}</span>\n                </Button>\n              </div>\n              <Input\n                id=\"sp-entity-id\"\n                :model-value=\"spEntityId\"\n                readonly\n                class=\"bg-muted/30 font-mono text-xs select-all\"\n              />\n              <p class=\"text-muted-foreground text-xs\">\n                The unique audience restriction URI identifying our service to your IdP.\n              </p>\n            </div>\n\n            <!-- Protocol Specifications Summary -->\n            <div class=\"border-border bg-muted/20 space-y-2 rounded-lg border p-3\">\n              <p class=\"text-foreground text-xs font-medium\">Protocol & Binding Requirements</p>\n              <div class=\"grid grid-cols-2 gap-2 text-xs\">\n                <div>\n                  <span class=\"text-muted-foreground block text-xs\">NameID Format</span>\n                  <span class=\"text-foreground font-mono text-xs font-medium\">EmailAddress (urn:oasis:...)</span>\n                </div>\n                <div>\n                  <span class=\"text-muted-foreground block text-xs\">SAML Binding</span>\n                  <span class=\"text-foreground font-mono text-xs font-medium\">HTTP-POST</span>\n                </div>\n                <div>\n                  <span class=\"text-muted-foreground block text-xs\">Signing Algorithm</span>\n                  <span class=\"text-foreground font-mono text-xs font-medium\">RSA-SHA256</span>\n                </div>\n                <div>\n                  <span class=\"text-muted-foreground block text-xs\">Assertion Encryption</span>\n                  <span class=\"text-foreground font-mono text-xs font-medium\">Optional (AES-256-GCM)</span>\n                </div>\n              </div>\n            </div>\n\n            <Separator />\n\n            <!-- SP Public Certificate -->\n            <div class=\"space-y-2\">\n              <div class=\"flex items-center justify-between\">\n                <label class=\"text-foreground text-xs font-medium\"> SP X.509 Public Certificate </label>\n                <div class=\"flex items-center gap-1.5\">\n                  <Button\n                    variant=\"ghost\"\n                    size=\"sm\"\n                    class=\"text-muted-foreground hover:text-foreground h-6 px-2 text-xs\"\n                    @click=\"copyText(spCertificatePem, 'sp-cert')\"\n                  >\n                    <Check v-if=\"copiedKey === 'sp-cert'\" class=\"text-success mr-1 size-3\" />\n                    <Copy v-else class=\"mr-1 size-3\" />\n                    <span>{{ copiedKey === 'sp-cert' ? 'Copied' : 'Copy PEM' }}</span>\n                  </Button>\n                  <Button\n                    aria-label=\"Download attachment\"\n                    variant=\"outline\"\n                    size=\"sm\"\n                    class=\"h-6 gap-1 px-2 text-xs\"\n                    @click=\"downloadSpCertificate\"\n                  >\n                    <Download class=\"size-3\" />\n                    <span>Download .crt</span>\n                  </Button>\n                </div>\n              </div>\n\n              <pre\n                class=\"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\"\n              ><code>{{ spCertificatePem }}</code></pre>\n              <p class=\"text-muted-foreground text-xs\">\n                Import this certificate into your IdP if assertion signing or encryption is required.\n              </p>\n            </div>\n          </CardContent>\n\n          <CardFooter\n            class=\"border-border text-muted-foreground flex items-center justify-between border-t pt-3 text-xs\"\n          >\n            <span class=\"flex items-center gap-1.5\">\n              <FileCode class=\"size-3.5\" />\n              <span>SP Metadata XML Endpoint</span>\n            </span>\n            <Button\n              variant=\"ghost\"\n              size=\"sm\"\n              class=\"text-primary hover:text-primary/80 h-6 gap-1 px-2 text-xs\"\n              @click=\"copyText(spMetadataXmlUrl, 'meta-url')\"\n            >\n              <span>{{ copiedKey === 'meta-url' ? 'Copied URL' : 'Copy metadata.xml URL' }}</span>\n              <ExternalLink class=\"size-3\" />\n            </Button>\n          </CardFooter>\n        </Card>\n      </div>\n\n      <!-- Right Column: Identity Provider (IdP) Metadata Form -->\n      <div class=\"space-y-6 lg:col-span-6\">\n        <Card class=\"border-border bg-card shadow-xs\">\n          <CardHeader class=\"pb-4\">\n            <div class=\"flex items-center justify-between\">\n              <div class=\"flex items-center gap-2\">\n                <div class=\"bg-primary/10 text-primary flex size-7 items-center justify-center rounded-md\">\n                  <Lock class=\"size-4\" />\n                </div>\n                <div>\n                  <CardTitle class=\"text-sm font-semibold\">Identity Provider (IdP) Metadata</CardTitle>\n                  <CardDescription class=\"text-xs\">\n                    Configure SAML endpoints and credentials provided by {{ currentPreset.name }}\n                  </CardDescription>\n                </div>\n              </div>\n              <Badge variant=\"secondary\" class=\"font-mono text-xs\">{{ currentPreset.name }}</Badge>\n            </div>\n          </CardHeader>\n\n          <CardContent class=\"space-y-4 pt-0\">\n            <!-- XML Upload Dropzone -->\n            <div\n              :class=\"\n                cn(\n                  'border-border relative flex cursor-pointer flex-col items-center justify-center rounded-xl border border-dashed p-4 text-center transition-colors',\n                  isDraggingXml\n                    ? 'border-primary bg-primary/10'\n                    : 'bg-muted/20 hover:border-primary/50 hover:bg-muted/40',\n                )\n              \"\n              @dragover=\"handleXmlDragOver\"\n              @dragleave=\"handleXmlDragLeave\"\n              @drop=\"handleXmlDrop\"\n              @click=\"simulateXmlUpload\"\n            >\n              <div class=\"bg-primary/10 text-primary mb-2 grid size-9 place-items-center rounded-full\">\n                <UploadCloud class=\"size-4\" />\n              </div>\n              <p class=\"text-foreground text-xs font-semibold\">\n                Drag & drop IdP <span class=\"font-mono\">metadata.xml</span> to auto-fill\n              </p>\n              <p class=\"text-muted-foreground mt-0.5 text-xs\">\n                Or click here to simulate parsing your Identity Provider metadata file\n              </p>\n            </div>\n\n            <!-- XML Notice -->\n            <div\n              v-if=\"xmlUploadedNotice\"\n              class=\"border-success/30 bg-success/10 text-success flex items-center gap-2 rounded-lg border p-2.5 text-xs\"\n            >\n              <CheckCircle2 class=\"size-4 shrink-0\" />\n              <span>Parsed SSO URL, Entity ID, and X.509 Certificate from metadata XML.</span>\n            </div>\n\n            <!-- IdP SSO URL -->\n            <div class=\"space-y-1.5\">\n              <label for=\"idp-sso-url\" class=\"text-foreground text-xs font-medium\">\n                IdP Single Sign-On (SSO) URL\n              </label>\n              <Input\n                id=\"idp-sso-url\"\n                v-model=\"idpSsoUrl\"\n                placeholder=\"https://company.okta.com/app/sso/saml\"\n                class=\"font-mono text-xs\"\n              />\n              <p class=\"text-muted-foreground text-xs\">\n                The HTTP-POST endpoint at your IdP where users are redirected for authentication.\n              </p>\n            </div>\n\n            <!-- IdP Entity ID / Issuer -->\n            <div class=\"space-y-1.5\">\n              <label for=\"idp-entity-id\" class=\"text-foreground text-xs font-medium\">\n                IdP Entity ID / Issuer URI\n              </label>\n              <Input\n                id=\"idp-entity-id\"\n                v-model=\"idpEntityId\"\n                placeholder=\"http://www.okta.com/exk849201\"\n                class=\"font-mono text-xs\"\n              />\n              <p class=\"text-muted-foreground text-xs\">\n                The unique issuer string provided in your IdP metadata document.\n              </p>\n            </div>\n\n            <!-- X.509 Certificate PEM Textarea -->\n            <div class=\"space-y-1.5\">\n              <div class=\"flex items-center justify-between\">\n                <label for=\"idp-cert-pem\" class=\"text-foreground text-xs font-medium\">\n                  IdP X.509 Signing Certificate (PEM)\n                </label>\n                <Badge variant=\"outline\" class=\"border-success/40 text-success font-mono text-xs\">\n                  Valid (Expires Oct 2028)\n                </Badge>\n              </div>\n              <Textarea\n                id=\"idp-cert-pem\"\n                v-model=\"idpCertPem\"\n                :rows=\"5\"\n                no-resize\n                class=\"border-border/70 font-mono text-xs leading-relaxed\"\n                placeholder=\"-----BEGIN CERTIFICATE-----...\"\n              />\n              <p class=\"text-muted-foreground text-xs\">\n                Public X.509 certificate used by our SP to verify the cryptographic signature on SAML assertions.\n              </p>\n            </div>\n\n            <Separator />\n\n            <!-- Domain Enforcement Switch -->\n            <div class=\"border-border bg-muted/20 flex items-start justify-between gap-3 rounded-lg border p-3\">\n              <div class=\"space-y-0.5\">\n                <div class=\"flex items-center gap-1.5\">\n                  <ShieldAlert class=\"text-primary size-4\" />\n                  <p class=\"text-foreground text-xs font-semibold\">Enforce SSO for all domain emails</p>\n                </div>\n                <p class=\"text-muted-foreground text-xs\">\n                  Require all users with <span class=\"text-foreground font-mono font-medium\">@company.com</span> email\n                  addresses to log in via SSO. Password logins will be blocked.\n                </p>\n              </div>\n              <Switch v-model=\"enforceDomain\" class=\"mt-1\" aria-label=\"Enforce SSO for domain\" />\n            </div>\n          </CardContent>\n\n          <CardFooter class=\"border-border flex flex-wrap items-center justify-between gap-2 border-t pt-4\">\n            <Button\n              variant=\"ghost\"\n              size=\"sm\"\n              class=\"text-muted-foreground hover:text-foreground text-xs\"\n              @click=\"handleSelectPreset(selectedPresetId)\"\n            >\n              Reset to Defaults\n            </Button>\n            <Button\n              size=\"sm\"\n              class=\"gap-1.5 text-xs font-medium shadow-xs\"\n              :disabled=\"isSaving || !idpSsoUrl.trim() || !idpEntityId.trim()\"\n              @click=\"handleSaveConfig\"\n            >\n              <RotateCw v-if=\"isSaving\" class=\"size-3.5 animate-spin\" />\n              <Save v-else class=\"size-3.5\" />\n              <span>{{ isSaving ? 'Saving...' : 'Save SSO Configuration' }}</span>\n            </Button>\n          </CardFooter>\n        </Card>\n      </div>\n    </div>\n\n    <!-- Test SAML Login Diagnostic Dialog -->\n    <Dialog :open=\"testModalOpen\" @update:open=\"testModalOpen = $event\">\n      <DialogContent class=\"sm:max-w-xl\">\n        <DialogHeader>\n          <div class=\"flex items-center gap-2\">\n            <div class=\"bg-primary/10 text-primary flex size-8 items-center justify-center rounded-lg\">\n              <UserCheck class=\"size-4\" />\n            </div>\n            <div>\n              <DialogTitle>SAML 2.0 Authentication Diagnostic</DialogTitle>\n              <DialogDescription> Live assertion handshake simulation with {{ currentPreset.name }} </DialogDescription>\n            </div>\n          </div>\n        </DialogHeader>\n\n        <div class=\"space-y-4 py-2\">\n          <!-- Handshake Status Banner -->\n          <div\n            class=\"border-success/30 bg-success/10 text-success flex items-center justify-between rounded-lg border p-3 text-xs\"\n          >\n            <div class=\"flex items-center gap-2\">\n              <CheckCircle2 class=\"size-4 shrink-0\" />\n              <div>\n                <p class=\"font-semibold\">HTTP 200 OK — SAML Handshake Validated</p>\n                <p class=\"text-xs opacity-90\">All signature envelopes matched X.509 certificate</p>\n              </div>\n            </div>\n            <Badge variant=\"success\" class=\"font-mono text-xs\">Success</Badge>\n          </div>\n\n          <!-- Step Verification Checklist -->\n          <div class=\"border-border bg-muted/20 space-y-2 rounded-lg border p-3 text-xs\">\n            <h4 class=\"text-foreground font-semibold\">Verification Pipeline</h4>\n            <div class=\"space-y-1.5\">\n              <div class=\"text-foreground flex items-center gap-2\">\n                <Check class=\"text-success size-3.5 shrink-0\" />\n                <span\n                  >1. AuthnRequest dispatched with SP signature to\n                  <code class=\"font-mono text-xs\">{{ idpSsoUrl }}</code></span\n                >\n              </div>\n              <div class=\"text-foreground flex items-center gap-2\">\n                <Check class=\"text-success size-3.5 shrink-0\" />\n                <span>2. SAMLResponse assertion received and verified with RSA-SHA256 signature</span>\n              </div>\n              <div class=\"text-foreground flex items-center gap-2\">\n                <Check class=\"text-success size-3.5 shrink-0\" />\n                <span\n                  >3. AudienceRestriction matched <code class=\"font-mono text-xs\">{{ spEntityId }}</code></span\n                >\n              </div>\n              <div class=\"text-foreground flex items-center gap-2\">\n                <Check class=\"text-success size-3.5 shrink-0\" />\n                <span>4. Assertion timestamp valid within 300s clock-skew tolerance window</span>\n              </div>\n            </div>\n          </div>\n\n          <!-- Asserted Claims Table -->\n          <div class=\"space-y-1.5\">\n            <h4 class=\"text-foreground text-xs font-semibold\">Asserted Identity Claims & Attributes</h4>\n            <div class=\"border-border bg-muted/10 overflow-hidden rounded-md border text-xs\">\n              <table class=\"w-full\">\n                <thead>\n                  <tr class=\"border-border bg-muted/40 text-muted-foreground border-b text-left font-medium\">\n                    <th class=\"p-2 text-xs\">Field</th>\n                    <th class=\"p-2 text-xs\">Sample Claim Value</th>\n                  </tr>\n                </thead>\n                <tbody class=\"divide-border divide-y font-mono text-xs\">\n                  <tr v-for=\"claim in simulatedClaims\" :key=\"claim.mappedField\" class=\"hover:bg-muted/20\">\n                    <td class=\"text-primary p-2 font-semibold\">{{ claim.mappedField }}</td>\n                    <td class=\"text-foreground max-w-[240px] truncate p-2\">{{ claim.sampleValue }}</td>\n                  </tr>\n                </tbody>\n              </table>\n            </div>\n          </div>\n        </div>\n\n        <DialogFooter>\n          <Button variant=\"outline\" size=\"sm\" @click=\"testModalOpen = false\">Close</Button>\n          <Button size=\"sm\" class=\"gap-1.5\" @click=\"openTestModal\">\n            <RotateCw class=\"size-3.5\" />\n            <span>Re-test Handshake</span>\n          </Button>\n        </DialogFooter>\n      </DialogContent>\n    </Dialog>\n  </div>\n</template>\n",
      "type": "registry:block",
      "target": "~/app/components/blocks/SsoIdentityProviderConfig.vue"
    }
  ],
  "dependencies": [
    "lucide-vue-next"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/badge.json",
    "https://uipkge.dev/r/vue/button.json",
    "https://uipkge.dev/r/vue/card.json",
    "https://uipkge.dev/r/vue/dialog.json",
    "https://uipkge.dev/r/vue/input.json",
    "https://uipkge.dev/r/vue/separator.json",
    "https://uipkge.dev/r/vue/switch.json",
    "https://uipkge.dev/r/vue/textarea.json"
  ],
  "description": "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.",
  "categories": [
    "security",
    "app",
    "form",
    "dashboard"
  ]
}