From 274880fd03a644703b40d851c4062aaab5ed0ecd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 30 Jul 2026 23:13:39 +0000 Subject: [PATCH] Merge pull request #132 from CyberDrain/dev fix: dev to hotfix Synced from CyberDrain/CIPP@f17f1df91e8695e7b42a29bf5018592cf20440eb --- package.json | 2 +- public/version.json | 2 +- .../CippFormPages/CippAddGroupForm.jsx | 15 ++ .../CippSettings/CippAppServiceDomains.jsx | 150 ++++++++++++------ .../CippSettings/CippSSOSettings.jsx | 37 +++++ 5 files changed, 156 insertions(+), 50 deletions(-) diff --git a/package.json b/package.json index cbc6313f941d..e438f69573df 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cipp", - "version": "10.7.4", + "version": "10.7.5", "author": "CIPP Contributors", "homepage": "https://cipp.app/", "bugs": { diff --git a/public/version.json b/public/version.json index 04aa3b874be4..15a45341185e 100644 --- a/public/version.json +++ b/public/version.json @@ -1,3 +1,3 @@ { - "version": "10.7.4" + "version": "10.7.5" } \ No newline at end of file diff --git a/src/components/CippFormPages/CippAddGroupForm.jsx b/src/components/CippFormPages/CippAddGroupForm.jsx index 1af49a1a64e4..63b5b3e98424 100644 --- a/src/components/CippFormPages/CippAddGroupForm.jsx +++ b/src/components/CippFormPages/CippAddGroupForm.jsx @@ -101,6 +101,21 @@ const CippAddGroupForm = (props) => { ]} /> + + + + + { const isApex = !isWildcard && labels.length <= 2; const asuidHost = isWildcard ? `asuid.${base}` : `asuid.${host}`; + // A CNAME alias proves ownership on its own, so subdomains don't get a TXT row up front — + // the ownership TXT is only needed for apex/A and wildcard domains (and proxied CNAMEs, + // which CheckDns detects and surfaces after the fact). return { host, isWildcard, isApex, recommendedType: isApex ? "A" : "CNAME", + asuidHost, records: [ - { - purpose: "Ownership", - type: "TXT", - host: asuidHost, - value: siteInfo?.CustomDomainVerificationId ?? "", - }, + ...(isApex || isWildcard + ? [ + { + purpose: "Ownership", + type: "TXT", + host: asuidHost, + value: siteInfo?.CustomDomainVerificationId ?? "", + }, + ] + : []), isApex - ? { purpose: "Alias", type: "A", host, value: siteInfo?.InboundIpAddress ?? "" } - : { purpose: "Alias", type: "CNAME", host, value: siteInfo?.DefaultHostName ?? "" }, + ? { + purpose: "Alias", + type: "A", + host, + value: siteInfo?.InboundIpAddress ?? "", + } + : { + purpose: "Alias", + type: "CNAME", + host, + value: siteInfo?.DefaultHostName ?? "", + }, ], }; }; @@ -137,7 +148,9 @@ const DomainWizard = ({ open, onClose, siteInfo, initialDomain }) => { const [certDone, setCertDone] = useState(false); const [dnsResult, setDnsResult] = useState(null); - const dnsCheck = ApiPostCall({ onResult: (body) => setDnsResult(body?.Results ?? null) }); + const dnsCheck = ApiPostCall({ + onResult: (body) => setDnsResult(body?.Results ?? null), + }); const bindingAction = ApiPostCall({ relatedQueryKeys: [LIST_QUERY_KEY], onResult: () => { @@ -186,6 +199,24 @@ const DomainWizard = ({ open, onClose, siteInfo, initialDomain }) => { const ownershipVerified = dnsResult?.OwnershipVerified ?? false; const aliasVerified = dnsResult?.AliasVerified ?? false; + const staleAsuid = dnsResult?.StaleAsuid ?? false; + const canProceed = dnsResult?.CanProceed ?? false; + + // CheckDns can promote the ownership TXT to required after the fact (proxied CNAME) — + // splice it in ahead of the alias row when the client-side plan didn't include it. + const visibleRecords = useMemo(() => { + const rows = [...plan.records]; + const hasOwnershipRow = rows.some((r) => r.purpose === "Ownership"); + if (!hasOwnershipRow && (dnsResult?.OwnershipRequired || staleAsuid)) { + rows.unshift({ + purpose: "Ownership", + type: "TXT", + host: dnsResult?.AsuidHost ?? plan.asuidHost, + value: siteInfo?.CustomDomainVerificationId ?? "", + }); + } + return rows; + }, [plan, dnsResult, staleAsuid, siteInfo]); const runDnsCheck = () => { dnsCheck.mutate({ @@ -220,7 +251,7 @@ const DomainWizard = ({ open, onClose, siteInfo, initialDomain }) => { {steps.map((label, idx) => ( {label} @@ -251,8 +282,9 @@ const DomainWizard = ({ open, onClose, siteInfo, initialDomain }) => { <> Create the following records at your DNS provider, then click{" "} - Check DNS. The {plan.recommendedType} alias record - is recommended for this domain type; Azure also accepts the other alias type. + Check DNS. The {plan.recommendedType} alias + record is recommended for this domain type; Azure also accepts the other alias + type. @@ -265,7 +297,7 @@ const DomainWizard = ({ open, onClose, siteInfo, initialDomain }) => { - {plan.records.map((r) => ( + {visibleRecords.map((r) => ( {r.purpose} @@ -275,7 +307,12 @@ const DomainWizard = ({ open, onClose, siteInfo, initialDomain }) => { {r.host} - + {r.value} {r.value ? : null} @@ -288,22 +325,32 @@ const DomainWizard = ({ open, onClose, siteInfo, initialDomain }) => {
{isWildcard && ( - Wildcard domains verify by ownership only; the alias is validated by Azure when the - binding is created. Note: App Service Managed Certificates do not support wildcard - domains — you will need to upload your own certificate for HTTPS. + Wildcard domains verify by ownership only; the alias is validated by Azure when + the binding is created. Note: App Service Managed Certificates do not support + wildcard domains — you will need to upload your own certificate for HTTPS. )} - {dnsResult && !ownershipVerified && ( + {staleAsuid && ( + + A TXT record at {dnsResult?.AsuidHost} exists with an outdated + verification ID. Remove it (or update it to the value shown + above) — a stale domain-verification record blocks Azure's validation even when + the alias record is correct. + + )} + {dnsResult && !staleAsuid && !canProceed && ( - The ownership TXT record hasn't propagated yet. DNS changes can take a few minutes.{" "} + {dnsResult.OwnershipRequired + ? "The required DNS records haven't propagated yet — DNS changes can take a few minutes. If your alias record is proxied (e.g. Cloudflare orange-cloud), Azure can't see it: either set it to DNS-only, or create the ownership TXT record shown above. " + : "The alias record hasn't propagated yet. DNS changes can take a few minutes. "} {dnsResult.AliasDetail} )} - {dnsResult && ownershipVerified && !aliasVerified && !isWildcard && ( + {dnsResult && canProceed && !aliasVerified && !isWildcard && ( - Ownership is verified. The alias record isn't visible yet — this is expected if the - record is proxied (e.g. Cloudflare orange-cloud). You can continue; Azure will make - the final check when the binding is created. + Ownership is verified. The alias record isn't visible yet — this is expected if + the record is proxied (e.g. Cloudflare orange-cloud). You can continue; Azure + will make the final check when the binding is created. )} {dnsCheck.isError && ( @@ -338,7 +385,8 @@ const DomainWizard = ({ open, onClose, siteInfo, initialDomain }) => { {certDone ? ( }> - {hostname} is fully configured and secured with a managed certificate. + {hostname} is fully configured and secured with a managed + certificate. ) : isWildcard ? ( @@ -348,8 +396,8 @@ const DomainWizard = ({ open, onClose, siteInfo, initialDomain }) => { ) : ( <> - Provision a free App Service Managed Certificate for {hostname} and - enable the SNI SSL binding. This can take a minute or two. + Provision a free App Service Managed Certificate for {hostname}{" "} + and enable the SNI SSL binding. This can take a minute or two. If the domain's alias is proxied through a CDN (e.g. Cloudflare orange-cloud), @@ -366,7 +414,10 @@ const DomainWizard = ({ open, onClose, siteInfo, initialDomain }) => { {activeStep > 0 && !managing && ( - )} @@ -384,11 +435,7 @@ const DomainWizard = ({ open, onClose, siteInfo, initialDomain }) => { > {dnsCheck.isPending ? "Checking..." : "Check DNS"} - @@ -499,7 +546,11 @@ export const CippAppServiceDomains = () => { - {row.Secured ? : } + {row.Secured ? ( + + ) : ( + + )} {sslStateLabel(row.SslState)} {row.HostNameType && ( @@ -533,11 +584,11 @@ export const CippAppServiceDomains = () => { - Map custom domains to the App Service that hosts this CIPP instance. Each domain needs a DNS - ownership record and an alias record, a hostname binding, and (optionally) a free managed - TLS certificate — the wizard walks through all three and can be reopened at any time to - finish or fix a domain. The default *.azurewebsites.net hostname always remains - available. + Map custom domains to the App Service that hosts this CIPP instance. Each domain needs a + DNS ownership record and an alias record, a hostname binding, and (optionally) a free + managed TLS certificate — the wizard walks through all three and can be reopened at any + time to finish or fix a domain. The default *.azurewebsites.net hostname + always remains available. @@ -565,8 +616,11 @@ export const CippAppServiceDomains = () => { value={siteInfo?.CustomDomainVerificationId} /> - Use the default hostname as the CNAME target for subdomains, the inbound IP as the A - record for apex domains, and the verification ID as the asuid TXT value. + Use the default hostname as the CNAME target for subdomains, and the inbound IP as + the A record for apex domains. The verification ID is only needed as an{" "} + asuid TXT value for apex, wildcard or proxied domains — a subdomain + CNAME verifies on its own, and a stale asuid record should be + removed. )} diff --git a/src/components/CippSettings/CippSSOSettings.jsx b/src/components/CippSettings/CippSSOSettings.jsx index cc944882da8e..7a3055c1a533 100644 --- a/src/components/CippSettings/CippSSOSettings.jsx +++ b/src/components/CippSettings/CippSSOSettings.jsx @@ -84,6 +84,15 @@ export const CippSSOSettings = () => { // (the original "Failed to create client secret after 5 attempts" bug). const showCreate = !isProvisioned && !canRepair; const isOrphanedError = statusKey === "error" && !hasAppId; + // Three states on purpose: warmup may not have attempted the grant yet, which is not the + // same as the tenant refusing it. Failure is a soft one — sign-in still works, users just + // see the consent prompt they see today. + const preconsentInfo = + data?.preconsented === true + ? { label: "Granted", color: "success" } + : data?.preconsented === false + ? { label: "Not Granted", color: "warning" } + : { label: "Not Checked", color: "default" }; const handleCreate = () => { ssoAction.mutate({ @@ -204,6 +213,34 @@ export const CippSSOSettings = () => { + {hasAppId && ( + <> + + + Admin Consent + + + + + {data?.preconsented === false && ( + + {data?.preconsentError + ? `Users will be prompted to consent at sign-in. ${data.preconsentError}` + : "Users will be prompted to consent at sign-in."} + + )} + + + )} + {data?.appId && ( <>