UNPKG

txt-domain-verification

Version:

A production-ready Node.js package for verifying domain ownership through DNS TXT records with authoritative nameserver queries, security features, and comprehensive error handling.

3 lines (2 loc) 3.3 kB
"use strict";var e=require("dns"),r=require("dns2"),t=require("crypto");function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var o=n(e),s=n(r),a=n(t);function i(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}const c=o.default,{Client:u}=s.default,l=a.default,f={VERIFICATION_PREFIX:"_verify",TOKEN_EXPIRY_HOURS:72,MAX_RETRIES:3,RETRY_DELAY_MS:1e3,DNS_TIMEOUT_MS:1e4,MIN_NAMESERVERS_VERIFIED:1};function m(e){if("string"!=typeof e)throw new Error("Domain must be a string");return e.trim().toLowerCase().replace(/\.$/,"")}function d(e,r,t,n=new Date){if(!e||!r||!t)throw new Error("Secret, userId, and domain are required");const o=`${r}|${t}|${n.toISOString()}`,s=l.createHmac("sha256",e);return s.update(o),s.digest("base64url")}async function E(e){const r=m(e);try{const e=await c.promises.resolveNs(r);if(!e||0===e.length)throw new Error(`No nameservers found for ${r}`);const t=[];for(const r of e)try{const e=await c.promises.resolve4(r);t.push(...e)}catch(e){console.warn(`Warning: Could not resolve nameserver ${r}: ${e.message}`)}if(0===t.length)throw new Error(`Could not resolve any nameserver IPs for ${r}`);return t}catch(e){throw new Error(`Failed to resolve nameservers for ${r}: ${e.message}`)}}async function w(e,r,t){return new Promise((n,o)=>{const s=new u({nameServers:[e],timeout:f.DNS_TIMEOUT_MS}),a=`${t}.${r}`;s.on("message",e=>{const r=(e.answers||[]).filter(e=>"TXT"===e.type).map(e=>e.data.join(""));n(r)}),s.on("error",r=>{o(new Error(`Nameserver query failed for ${e}: ${r.message}`))}),s.on("timeout",()=>{o(new Error(`Nameserver query timeout for ${e}`))}),s.query(a,"TXT")})}var v=i({generateVerificationCode:function(e,r,t,n="",o="{{businessName}} Domain Verification: {{token}}",s=new Date){const a=m(e),i=d(r,t,a,s),c=o.replace("{{businessName}}",n).replace("{{token}}",i);return{token:i,formattedString:c,domain:a,issuedAt:s,expiresAt:new Date(s.getTime()+60*f.TOKEN_EXPIRY_HOURS*60*1e3),instructions:`Please add the following TXT record to your DNS settings for ${a}:\nName: ${f.VERIFICATION_PREFIX}\nValue: ${c}`,verificationRecord:{name:`${f.VERIFICATION_PREFIX}.${a}`,value:c}}},verifyDomain:async function(e,r,t=f.VERIFICATION_PREFIX,n={}){const o=m(e),s=n.maxRetries||f.MAX_RETRIES,a=n.retryDelay||f.RETRY_DELAY_MS;let i;for(let e=1;e<=s;e++)try{const n=await E(o),s=n.map(async e=>{try{return{nameserver:e,records:await w(e,o,t),success:!0}}catch(r){return{nameserver:e,error:r.message,success:!1}}}),a=await Promise.all(s),c=a.filter(e=>e.success),u=a.filter(e=>!e.success);let l=!1,f=[];for(const e of c)for(const t of e.records)t.includes(r)&&(l=!0,f.push({nameserver:e.nameserver,record:t}));const m={verified:l,domain:o,attempt:e,totalNameservers:n.length,successfulQueries:c.length,failedQueries:u.length,matchingRecords:f,partialPropagation:c.length>0&&c.length<n.length,nameserverResults:a,timestamp:new Date};if(l)return m;if(c.length>0)return m;i=new Error(`All nameserver queries failed for ${o}`)}catch(r){if(i=r,e<s){const r=a*Math.pow(2,e-1);await new Promise(e=>setTimeout(e,r))}}throw i||new Error(`Verification failed after ${s} attempts`)},generateSecureToken:d,normalizeDomain:m,resolveAuthoritativeNameservers:E,CONFIG:f});module.exports=v; //# sourceMappingURL=index.js.map