1import{j as e,r as n}from"./vendor-query-Dkm4furz.js";import{B,C as k,S as z,a as p,I as u,U as X,t as c,b as h}from"./index-Bxl4mrbd.js";import{L as d}from"./label-BTG2IkYA.js";import{T as J}from"./textarea-D2mFGo0m.js";import{S as G}from"./switch-CIeEVNbk.js";import{C as E}from"./circle-check-DcZxd5Xc.js";import{T as U}from"./triangle-alert-B8mDITLe.js";import{C as D}from"./copy-yJYm7TOm.js";import"./vendor-react-C4nmQqxk.js";import"./vendor-pdf-5g-lfC8S.js";import"./vendor-charts-DHhRtk-9.js";import"./vendor-supabase-CbPlSJWC.js";import"./index-CDTJsEQL.js";import"./index-C56ocfua.js";function oe(){return e.jsx(B,{pageTitle:"Single sign-on",minRole:"admin",children:a=>e.jsxs(e.Fragment,{children:[e.jsx(H,{orgId:a.orgId}),e.jsx(W,{orgId:a.orgId})]})})}function P(a){if(!a)return"never";const s=Date.now()-new Date(a).getTime();return s<6e4?"just now":s<36e5?`${Math.floor(s/6e4)}m ago`:s<864e5?`${Math.floor(s/36e5)}h ago`:`${Math.floor(s/864e5)}d ago`}function W({orgId:a}){const[s,f]=n.useState(null),[N,g]=n.useState(!0),[x,o]=n.useState(!1),[m,v]=n.useState(null),w=async()=>{g(!0);const l=await h(`/business/orgs/${a}/scim`);if(l.ok){const r=await l.json();f(r.status)}g(!1)};n.useEffect(()=>{w()},[a]);const y=async()=>{o(!0);const l=await h(`/business/orgs/${a}/scim/token`,{method:"POST"}),r=await l.json().catch(()=>({}));l.ok?r.rawToken&&r.status&&(v(r.rawToken),f(r.status),c({title:"SCIM token created",description:"Copy it now â it won't be shown again."})):c({title:"Failed to create SCIM token",description:r.error??"",variant:"destructive"}),o(!1)},S=async()=>{if(!confirm("Revoke the SCIM token? Your IdP will stop syncing until you generate a new one."))return;o(!0);const l=await h(`/business/orgs/${a}/scim/token`,{method:"DELETE"}),r=await l.json().catch(()=>({}));l.ok?r.status&&(f(r.status),v(null),c({title:"SCIM token revoked"})):c({title:"Failed to revoke",description:r.error??"",variant:"destructive"}),o(!1)},C=typeof window<"u"?window.location.origin:"",j=s?`${C}${s.endpointPath}`:"";return e.jsxs(k,{className:"p-5 mt-6",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-3",children:[e.jsx(X,{className:"w-4 h-4 text-accent"}),e.jsx("h2",{className:"font-semibold",children:"SCIM 2.0 Directory Sync"})]}),e.jsx("p",{className:"text-sm text-muted-foreground mb-4",children:"Auto-provision and deprovision teammates from Okta, Azure AD, Google Workspace, or any other SCIM 2.0 identity provider. Adding or removing a person in your IdP creates or revokes their Futurecast membership within minutes."}),N&&e.jsx("p",{className:"text-sm text-muted-foreground",children:"Loadingâ¦"}),!N&&s&&e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"grid gap-3 sm:grid-cols-2 text-sm",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-muted-foreground",children:"Status"}),e.jsx("div",{children:s.connected?e.jsxs("span",{className:"inline-flex items-center gap-1 text-emerald-600",children:[e.jsx(E,{className:"w-3 h-3"})," Connected"]}):e.jsxs("span",{className:"inline-flex items-center gap-1 text-amber-600",children:[e.jsx(U,{className:"w-3 h-3"})," Not connected"]})})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-muted-foreground",children:"Last sync"}),e.jsx("div",{title:s.lastSyncedAt??"",children:P(s.lastSyncedAt)})]}),s.connected&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-muted-foreground",children:"Token prefix"}),e.jsxs("code",{className:"text-xs",children:[s.tokenPrefix,"â¦"]})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-muted-foreground",children:"Created"}),e.jsx("div",{children:P(s.createdAt)})]})]})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx(d,{children:"SCIM endpoint URL"}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(u,{value:j,readOnly:!0,className:"font-mono text-xs"}),e.jsx(p,{variant:"outline",size:"sm",onClick:()=>{navigator.clipboard.writeText(j),c({title:"Endpoint copied"})},children:e.jsx(D,{className:"w-3 h-3"})})]}),e.jsx("p",{className:"text-xs text-muted-foreground",children:"Paste this URL into the SCIM 2.0 connector in your IdP."})]}),m&&e.jsxs("div",{className:"space-y-1",children:[e.jsx(d,{children:"Bearer token (shown once)"}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(u,{value:m,readOnly:!0,className:"font-mono text-xs"}),e.jsx(p,{variant:"outline",size:"sm",onClick:()=>{navigator.clipboard.writeText(m),c({title:"Token copied"})},children:e.jsx(D,{className:"w-3 h-3"})})]}),e.jsx("p",{className:"text-xs text-amber-600",children:"Save this token now â we don't store it in plain text and won't show it again."})]}),e.jsxs("div",{className:"flex gap-2 pt-2",children:[e.jsx(p,{onClick:y,disabled:x,children:s.connected?"Rotate token":"Generate SCIM token"}),s.connected&&e.jsx(p,{variant:"outline",onClick:S,disabled:x,children:"Revoke"})]})]})]})}function H({orgId:a}){const[s,f]=n.useState(null),[N,g]=n.useState(!0),[x,o]=n.useState(!1),[m,v]=n.useState("saml"),[w,y]=n.useState(""),[S,C]=n.useState(""),[j,l]=n.useState(""),[r,M]=n.useState(""),[O,T]=n.useState(""),[A,L]=n.useState(""),b=async()=>{g(!0);const t=await h(`/business/orgs/${a}/sso`);if(t.ok){const i=await t.json();f(i.config),i.config&&(v(i.config.protocol),y(i.config.emailDomain),M(i.config.oidcIssuer??""),T(i.config.oidcClientId??""))}g(!1)};n.useEffect(()=>{b()},[a]);const $=async()=>{o(!0);const t={protocol:m,emailDomain:w};m==="saml"?(S&&(t.samlMetadataXml=S),j&&(t.samlMetadataUrl=j)):(t.oidcIssuer=r,t.oidcClientId=O,t.oidcClientSecret=A);const i=await h(`/business/orgs/${a}/sso`,{method:"PUT",body:JSON.stringify(t)});if(i.ok)c({title:"SSO configuration saved"}),C(""),L(""),await b();else{const I=await i.json().catch(()=>({}));c({title:"Failed to save",description:I.error??"",variant:"destructive"})}o(!1)},R=async()=>{o(!0);const t=await h(`/business/orgs/${a}/sso/activate`,{method:"POST"}),i=await t.json().catch(()=>({}));t.ok?(c({title:i.stub?"Activated in preview mode":"IdP activated
1with Supabase",description:i.stub?"Supabase access token not configured â discovery will return a clear error until activated for real.":void 0}),await b()):c({title:"Activation failed",description:i.error??"",variant:"destructive"}),o(!1)},F=async t=>{o(!0);const i=await h(`/business/orgs/${a}/sso/enforce`,{method:"POST",body:JSON.stringify({enforce:t})}),I=await i.json().catch(()=>({}));i.ok?await b():c({title:"Failed to toggle enforcement",description:I.error??"",variant:"destructive"}),o(!1)};return N?e.jsx("p",{className:"text-sm text-muted-foreground",children:"Loadingâ¦"}):e.jsxs("div",{className:"space-y-6",children:[e.jsxs("p",{className:"text-sm text-muted-foreground",children:["Configure SAML or OIDC single sign-on for your organization. Once activated and enforced, members of ",e.jsx("strong",{children:(s==null?void 0:s.emailDomain)??"your domain"})," will sign in through your identity provider; password sign-in will be refused."]}),s&&e.jsx(k,{className:"p-5",children:e.jsxs("div",{className:"flex items-start justify-between gap-4",children:[e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center gap-2 mb-1",children:[e.jsx(z,{className:"w-4 h-4 text-accent"}),e.jsx("span",{className:"font-semibold",children:"Current configuration"})]}),e.jsxs("dl",{className:"text-sm text-muted-foreground space-y-1",children:[e.jsxs("div",{children:[e.jsx("dt",{className:"inline text-foreground",children:"Protocol:"})," ",s.protocol.toUpperCase()]}),e.jsxs("div",{children:[e.jsx("dt",{className:"inline text-foreground",children:"Email domain:"})," ",s.emailDomain]}),e.jsxs("div",{children:[e.jsx("dt",{className:"inline text-foreground",children:"Status:"})," ",s.status==="active"?e.jsxs("span",{className:"inline-flex items-center gap-1 text-emerald-600",children:[e.jsx(E,{className:"w-3 h-3"})," Active"]}):e.jsxs("span",{className:"inline-flex items-center gap-1 text-amber-600",children:[e.jsx(U,{className:"w-3 h-3"})," Draft"]})]}),s.protocol==="oidc"&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{children:[e.jsx("dt",{className:"inline text-foreground",children:"OIDC issuer:"})," ",s.oidcIssuer]}),e.jsxs("div",{children:[e.jsx("dt",{className:"inline text-foreground",children:"OIDC client id:"})," ",s.oidcClientId]})]})]})]}),e.jsxs("div",{className:"flex flex-col items-end gap-2",children:[s.status==="draft"&&e.jsx(p,{onClick:R,disabled:x,children:"Activate with Supabase"}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(d,{htmlFor:"enforce",className:"text-sm",children:"Enforce SSO"}),e.jsx(G,{id:"enforce",checked:s.enforceSso,onCheckedChange:F,disabled:x||s.status!=="active"})]})]})]})}),e.jsxs(k,{className:"p-5 space-y-4",children:[e.jsx("h2",{className:"font-semibold",children:s?"Update configuration":"Add configuration"}),e.jsxs("div",{className:"grid gap-4 sm:grid-cols-2",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx(d,{children:"Protocol"}),e.jsxs("select",{className:"border rounded-md h-10 px-3 w-full bg-background",value:m,onChange:t=>v(t.target.value),children:[e.jsx("option",{value:"saml",children:"SAML 2.0"}),e.jsx("option",{value:"oidc",children:"OIDC"})]})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx(d,{children:"Email domain"}),e.jsx(u,{value:w,onChange:t=>y(t.target.value),placeholder:"acme.com"})]})]}),m==="saml"?e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx(d,{children:"SAML metadata URL (optional)"}),e.jsx(u,{value:j,onChange:t=>l(t.target.value),placeholder:"https://idp.example.com/saml/metadata"})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx(d,{children:"SAML metadata XML"}),e.jsx(J,{rows:6,value:S,onChange:t=>C(t.target.value),placeholder:"<EntityDescriptor xmlns=...>",className:"font-mono text-xs"}),(s==null?void 0:s.hasSamlMetadata)&&e.jsx("p",{className:"text-xs text-muted-foreground",children:"Metadata is currently configured. Submitting a new value replaces it."})]})]}):e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx(d,{children:"OIDC issuer URL"}),e.jsx(u,{value:r,onChange:t=>M(t.target.value),placeholder:"https://idp.example.com"})]}),e.jsxs("div",{className:"grid gap-3 sm:grid-cols-2",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx(d,{children:"Client id"}),e.jsx(u,{value:O,onChange:t=>T(t.target.value)})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx(d,{children:"Client secret"}),e.jsx(u,{type:"password",value:A,onChange:t=>L(t.target.value),placeholder:s?"(unchanged)":""})]})]})]}),e.jsx("div",{className:"flex gap-2",children:e.jsx(p,{onClick:$,disabled:x,children:x?"Savingâ¦":"Save configuration"})})]}),e.jsxs(k,{className:"p-5",children:[e.jsx("h2",{className:"font-semibold mb-2",children:"How sign-in works"}),e.jsxs("ol",{className:"list-decimal pl-5 text-sm text-muted-foreground space-y-1",children:[e.jsxs("li",{children:["A user enters their email at ",e.jsx("code",{children:"/login"}),"."]}),e.jsx("li",{children:"The web app calls our discovery endpoint with that email."}),e.jsxs("li",{children:["If the email's domain matches an SSO-enforced org, the app calls Supabase's ",e.jsx("code",{children:"signInWithSSO"})," and redirects to your IdP."]}),e.jsx("li",{children:"After your IdP authenticates the user, Supabase issues a session and the user lands in the dashboard."}),e.jsx("li",{children:"Password sign-in is refused for any user whose email domain has SSO enforced."})]})]})]})}export{oe as default};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.