@authhero/widget 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authhero-widget/authhero-widget.esm.js +1 -1
- package/dist/authhero-widget/p-1625b214.entry.js +1 -0
- package/dist/authhero-widget/p-55e6c943.entry.js +1 -0
- package/dist/cjs/authhero-node.cjs.entry.js +15 -1
- package/dist/cjs/authhero-widget.cjs.entry.js +134 -1
- package/dist/collection/components/authhero-node/authhero-node.js +15 -1
- package/dist/collection/components/authhero-widget/authhero-widget.js +2 -1
- package/dist/collection/utils/sanitize-html.js +132 -0
- package/dist/components/authhero-node.js +1 -1
- package/dist/components/authhero-widget.js +1 -1
- package/dist/components/p-Cuu5Lfc5.js +1 -0
- package/dist/esm/authhero-node.entry.js +15 -1
- package/dist/esm/authhero-widget.entry.js +134 -1
- package/dist/types/components/authhero-node/authhero-node.d.ts +5 -0
- package/dist/types/utils/sanitize-html.d.ts +20 -0
- package/hydrate/index.js +149 -2
- package/hydrate/index.mjs +149 -2
- package/package.json +1 -1
- package/dist/authhero-widget/p-30808298.entry.js +0 -1
- package/dist/authhero-widget/p-3ae71c86.entry.js +0 -1
- package/dist/components/p-DITKGXA_.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t,p as e,H as r,c as i,h as o}from"./p-BbVVe_wV.js";const a=e(class extends r{constructor(t){super(),!1!==t&&this.__registerHost(),this.__attachShadow(),this.fieldChange=i(this,"fieldChange"),this.buttonClick=i(this,"buttonClick")}component;value;disabled=!1;passwordVisible=!1;fieldChange;buttonClick;handleInput=t=>{this.fieldChange.emit({id:this.component.id,value:t.target.value})};handleCheckbox=t=>{this.fieldChange.emit({id:this.component.id,value:t.target.checked?"true":"false"})};sanitizeForCssToken(t){return t.toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")}handleButtonClick=(t,e,r)=>{"submit"!==e&&t.preventDefault(),this.buttonClick.emit({id:this.component.id,type:e,value:r})};togglePasswordVisibility=()=>{this.passwordVisible=!this.passwordVisible};getErrors(){const t=this.component;return t.messages?.filter((t=>"error"===t.type))||[]}renderFloatingLabel(t,e,r,i){return t?o("label",{class:{"input-label":!0,floating:!!i},part:"label",htmlFor:e},t,r&&o("span",{class:"required"},"*")):null}renderLabel(t,e,r){return t?o("label",{class:"input-label",part:"label",htmlFor:e},t,r&&o("span",{class:"required"},"*")):null}renderPasswordToggle(){return this.passwordVisible?o("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"},o("path",{d:"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"}),o("line",{x1:"1",y1:"1",x2:"23",y2:"23"})):o("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"},o("path",{d:"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"}),o("circle",{cx:"12",cy:"12",r:"3"}))}renderErrors(){return this.getErrors().map((t=>o("span",{class:"error-text",part:"error-text",key:t.id??t.text},t.text)))}renderHint(t){return t?o("span",{class:"helper-text",part:"helper-text"},t):null}renderDivider(){return o("hr",{class:"divider",part:"divider"})}renderHtml(t){return o("div",{class:"html-content",part:"html-content",innerHTML:t.config?.content??""})}renderImage(t){const{src:e,alt:r,width:i,height:a}=t.config??{};return e?o("img",{class:"image",part:"image",src:e,alt:r??"",width:i,height:a,loading:"lazy"}):null}renderRichText(t){return o("div",{class:"rich-text",part:"rich-text",innerHTML:t.config?.content??""})}renderNextButton(t){return o("button",{type:"submit",class:"btn btn-primary",part:"button button-primary",disabled:this.disabled,onClick:t=>this.handleButtonClick(t,"submit","next")},t.config.text??"Continue")}renderPreviousButton(t){return o("button",{type:"button",class:"btn btn-secondary",part:"button button-secondary",disabled:this.disabled,onClick:t=>this.handleButtonClick(t,"previous","back")},t.config.text??"Back")}renderJumpButton(t){return o("button",{type:"button",class:"btn btn-link",part:"button button-link",disabled:this.disabled,onClick:e=>this.handleButtonClick(e,"jump",t.config.target_step)},t.config.text??"Go")}renderResendButton(t){return o("button",{type:"button",class:"btn btn-link",part:"button button-link",disabled:this.disabled,onClick:e=>this.handleButtonClick(e,"resend",t.config.resend_action)},t.config.text??"Resend")}renderTextField(t){const e=`input-${t.id}`,r=this.getErrors(),{multiline:i,max_length:a}=t.config??{},s=!!(this.value&&this.value.length>0);return i?o("div",{class:"input-wrapper",part:"input-wrapper"},this.renderLabel(t.label,e,t.required),o("textarea",{id:e,class:{"input-field":!0,"has-error":r.length>0},part:"input textarea",name:t.id,placeholder:" ",required:t.required,disabled:this.disabled,maxLength:a,onInput:this.handleInput},this.value??""),this.renderErrors(),0===r.length&&this.renderHint(t.hint)):o("div",{class:"input-wrapper",part:"input-wrapper"},o("div",{class:"input-container"},o("input",{id:e,class:{"input-field":!0,"has-error":r.length>0},part:"input",type:t.sensitive?"password":"text",name:t.id,value:this.value??"",placeholder:" ",required:t.required,disabled:this.disabled,maxLength:a,onInput:this.handleInput}),this.renderFloatingLabel(t.label,e,t.required,s)),this.renderErrors(),0===r.length&&this.renderHint(t.hint))}renderEmailField(t){const e=`input-${t.id}`,r=this.getErrors(),i=!!(this.value&&this.value.length>0);return o("div",{class:"input-wrapper",part:"input-wrapper"},o("div",{class:"input-container"},o("input",{id:e,class:{"input-field":!0,"has-error":r.length>0},part:"input",type:"email",name:t.id,value:this.value??"",placeholder:" ",required:t.required,disabled:this.disabled,autocomplete:"email",onInput:this.handleInput}),this.renderFloatingLabel(t.label,e,t.required,i)),this.renderErrors(),0===r.length&&this.renderHint(t.hint))}renderPasswordField(t){const e=`input-${t.id}`,r=this.getErrors(),i=!!(this.value&&this.value.length>0),a=t.config?.forgot_password_link;return o("div",{class:"input-wrapper",part:"input-wrapper"},o("div",{class:"input-container password-container"},o("input",{id:e,class:{"input-field":!0,"has-error":r.length>0},part:"input",type:this.passwordVisible?"text":"password",name:t.id,value:this.value??"",placeholder:" ",required:t.required,disabled:this.disabled,minLength:t.config?.min_length,autocomplete:"current-password",onInput:this.handleInput}),this.renderFloatingLabel(t.label,e,t.required,i),o("button",{type:"button",class:"password-toggle",part:"password-toggle",onClick:this.togglePasswordVisibility,"aria-label":"Toggle password visibility","aria-pressed":this.passwordVisible?"true":"false"},this.renderPasswordToggle())),this.renderErrors(),0===r.length&&this.renderHint(t.hint),a&&o("div",{class:"field-link",part:"field-link"},o("a",{href:a,class:"link",part:"link"},"Forgot password?")))}renderNumberField(t){const e=`input-${t.id}`,r=this.getErrors(),{placeholder:i,min:a,max:s,step:n}=t.config??{};return o("div",{class:"input-wrapper",part:"input-wrapper"},this.renderLabel(t.label,e,t.required),o("input",{id:e,class:{"input-field":!0,"has-error":r.length>0},part:"input",type:"number",name:t.id,value:this.value??"",placeholder:i,required:t.required,disabled:this.disabled,min:a,max:s,step:n,onInput:this.handleInput}),this.renderErrors(),0===r.length&&this.renderHint(t.hint))}renderTelField(t){const e=`input-${t.id}`,r=this.getErrors();return o("div",{class:"input-wrapper",part:"input-wrapper"},this.renderLabel(t.label,e,t.required),o("input",{id:e,class:{"input-field":!0,"has-error":r.length>0},part:"input",type:"tel",name:t.id,value:this.value??"",placeholder:t.config?.placeholder,required:t.required,disabled:this.disabled,autocomplete:"tel",onInput:this.handleInput}),this.renderErrors(),0===r.length&&this.renderHint(t.hint))}renderUrlField(t){const e=`input-${t.id}`,r=this.getErrors();return o("div",{class:"input-wrapper",part:"input-wrapper"},this.renderLabel(t.label,e,t.required),o("input",{id:e,class:{"input-field":!0,"has-error":r.length>0},part:"input",type:"url",name:t.id,value:this.value??"",placeholder:t.config?.placeholder,required:t.required,disabled:this.disabled,onInput:this.handleInput}),this.renderErrors(),0===r.length&&this.renderHint(t.hint))}renderDateField(t){const e=`input-${t.id}`,r=this.getErrors(),{min:i,max:a}=t.config??{};return o("div",{class:"input-wrapper",part:"input-wrapper"},this.renderLabel(t.label,e,t.required),o("input",{id:e,class:{"input-field":!0,"has-error":r.length>0},part:"input",type:"date",name:t.id,value:this.value??"",required:t.required,disabled:this.disabled,min:i,max:a,onInput:this.handleInput}),this.renderErrors(),0===r.length&&this.renderHint(t.hint))}renderBooleanField(t){return o("label",{class:"checkbox-wrapper",part:"checkbox-wrapper"},o("input",{type:"checkbox",part:"checkbox",name:t.id,checked:"true"===this.value||!0===t.config?.default_value,required:t.required,disabled:this.disabled,onChange:this.handleCheckbox}),o("span",{class:"checkbox-label",part:"checkbox-label"},t.label))}renderLegalField(t){const e=t.config?.text??t.label??"",r=!0===t.config?.html;return o("label",{class:"checkbox-wrapper",part:"checkbox-wrapper"},o("input",{type:"checkbox",part:"checkbox",name:t.id,checked:"true"===this.value,required:t.required,disabled:this.disabled,onChange:this.handleCheckbox}),r?o("span",{class:"checkbox-label",part:"checkbox-label",innerHTML:e}):o("span",{class:"checkbox-label",part:"checkbox-label"},e))}renderDropdownField(t){const e=`input-${t.id}`,r=this.getErrors(),{options:i,placeholder:a}=t.config??{};return o("div",{class:"input-wrapper",part:"input-wrapper"},this.renderLabel(t.label,e,t.required),o("select",{id:e,class:{"input-field":!0,"has-error":r.length>0},part:"input select",name:t.id,required:t.required,disabled:this.disabled,onChange:this.handleInput},a&&o("option",{value:"",disabled:!0,selected:!this.value},a),i?.map((t=>o("option",{value:t.value,selected:this.value===t.value,key:t.value},t.label)))),this.renderErrors(),0===r.length&&this.renderHint(t.hint))}renderChoiceField(t){const e=this.getErrors(),{options:r,display:i}=t.config??{},a="checkbox"===i,s=a?"checkbox":"radio";return o("div",{class:"choice-wrapper",part:"choice-wrapper"},t.label&&o("span",{class:"choice-label",part:"choice-label"},t.label,t.required&&o("span",{class:"required"},"*")),o("div",{class:"choice-options",part:"choice-options"},r?.map((e=>o("label",{class:"choice-option",part:"choice-option",key:e.value},o("input",{type:s,part:s,name:t.id,value:e.value,checked:this.value===e.value,required:t.required&&!a,disabled:this.disabled,onChange:this.handleInput}),o("span",null,e.label))))),this.renderErrors(),0===e.length&&this.renderHint(t.hint))}renderSocialField(t){const e=t.config?.providers??[],r=t.config?.provider_details,i=new Map(r?.map((t=>[t.name,t]))??[]),a=["google-oauth2","google","facebook","apple","github","microsoft","windowslive","linkedin","vipps"];return o("div",{class:"social-buttons",part:"social-buttons"},e.map((t=>{const e=this.sanitizeForCssToken(t);return o("button",{type:"button",class:`btn btn-secondary btn-social btn-social-${e}`,part:`button button-secondary button-social button-social-${e}`,"data-provider":t,disabled:this.disabled,onClick:e=>this.handleButtonClick(e,"SOCIAL",t),key:t},(t=>{const e=i.get(t);if(e?.icon_url)return o("img",{class:"social-icon",src:e.icon_url,alt:e.display_name||t});const r=((t,e)=>{const r=t.toLowerCase(),i=e?.toLowerCase();if(i&&a.includes(i))return i;if(a.includes(r))return r;for(const t of a)if(r.includes(t))return t;return null})(t,e?.strategy)||t.toLowerCase();return"google-oauth2"===r||"google"===r?o("svg",{class:"social-icon",viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},o("path",{d:"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z",fill:"#4285F4"}),o("path",{d:"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z",fill:"#34A853"}),o("path",{d:"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z",fill:"#FBBC05"}),o("path",{d:"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z",fill:"#EA4335"})):"facebook"===r?o("svg",{class:"social-icon",viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},o("path",{d:"M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z",fill:"#1877F2"})):"apple"===r?o("svg",{class:"social-icon",viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},o("path",{d:"M17.05 20.28c-.98.95-2.05.8-3.08.35-1.09-.46-2.09-.48-3.24 0-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09l.01-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z",fill:"#000000"})):"github"===r?o("svg",{class:"social-icon",viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},o("path",{d:"M12 0C5.374 0 0 5.373 0 12c0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23A11.509 11.509 0 0112 5.803c1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z",fill:"#181717"})):"microsoft"===r||"windowslive"===r?o("svg",{class:"social-icon",viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},o("path",{d:"M0 0h11.377v11.372H0V0z",fill:"#f25022"}),o("path",{d:"M12.623 0H24v11.372H12.623V0z",fill:"#7fba00"}),o("path",{d:"M0 12.623h11.377V24H0v-11.377z",fill:"#00a4ef"}),o("path",{d:"M12.623 12.623H24V24H12.623v-11.377z",fill:"#ffb900"})):"linkedin"===r?o("svg",{class:"social-icon",viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},o("path",{d:"M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z",fill:"#0A66C2"})):"vipps"===r?o("svg",{class:"social-icon",viewBox:"0 0 48 48",xmlns:"http://www.w3.org/2000/svg"},o("path",{fill:"#FF5B24",d:"M3.5,8h41c1.9,0,3.5,1.6,3.5,3.5v25c0,1.9-1.6,3.5-3.5,3.5h-41C1.6,40,0,38.4,0,36.5v-25C0,9.6,1.6,8,3.5,8z"}),o("path",{fill:"#FFFFFF",d:"M27.9,20.3c1.4,0,2.6-1,2.6-2.5c0-1.5-1.2-2.5-2.6-2.5c-1.4,0-2.6,1-2.6,2.5C25.3,19.2,26.5,20.3,27.9,20.3z"}),o("path",{fill:"#FFFFFF",d:"M31.2,24.4c-1.7,2.2-3.5,3.8-6.7,3.8c-3.2,0-5.8-2-7.7-4.8c-0.8-1.2-2-1.4-2.9-0.8c-0.8,0.6-1,1.8-0.3,2.9c2.7,4.1,6.5,6.6,10.9,6.6c4,0,7.2-2,9.6-5.2c0.9-1.2,0.9-2.5,0-3.1C33.3,22.9,32.1,23.2,31.2,24.4z"})):o("svg",{class:"social-icon",viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},o("circle",{cx:"12",cy:"12",r:"10",fill:"none",stroke:"#666","stroke-width":"2"}),o("path",{d:"M2 12h20M12 2c-2.5 2.5-4 5.5-4 10s1.5 7.5 4 10c2.5-2.5 4-5.5 4-10s-1.5-7.5-4-10z",fill:"none",stroke:"#666","stroke-width":"2"}))})(t),o("span",{part:"button-social-text"},`Continue with ${(t=>{const e=i.get(t);return e?.display_name?e.display_name:{"google-oauth2":"Google",facebook:"Facebook",twitter:"Twitter",github:"GitHub",linkedin:"LinkedIn",apple:"Apple",microsoft:"Microsoft",windowslive:"Microsoft",amazon:"Amazon",dropbox:"Dropbox",bitbucket:"Bitbucket",spotify:"Spotify",slack:"Slack",discord:"Discord",twitch:"Twitch",line:"LINE",shopify:"Shopify",paypal:"PayPal","paypal-sandbox":"PayPal",box:"Box",salesforce:"Salesforce","salesforce-sandbox":"Salesforce",yahoo:"Yahoo",auth0:"Auth0",vipps:"Vipps"}[t.toLowerCase()]||t.split("-").map((t=>t.charAt(0).toUpperCase()+t.slice(1))).join(" ")})(t)}`))})))}render(){if(!this.component)return null;if(!1===this.component.visible)return null;switch(this.component.type){case"DIVIDER":return this.renderDivider();case"HTML":return this.renderHtml(this.component);case"IMAGE":return this.renderImage(this.component);case"RICH_TEXT":return this.renderRichText(this.component);case"NEXT_BUTTON":return this.renderNextButton(this.component);case"PREVIOUS_BUTTON":return this.renderPreviousButton(this.component);case"JUMP_BUTTON":return this.renderJumpButton(this.component);case"RESEND_BUTTON":return this.renderResendButton(this.component);case"TEXT":return this.renderTextField(this.component);case"EMAIL":return this.renderEmailField(this.component);case"PASSWORD":return this.renderPasswordField(this.component);case"NUMBER":return this.renderNumberField(this.component);case"TEL":return this.renderTelField(this.component);case"URL":return this.renderUrlField(this.component);case"DATE":return this.renderDateField(this.component);case"BOOLEAN":return this.renderBooleanField(this.component);case"LEGAL":return this.renderLegalField(this.component);case"DROPDOWN":return this.renderDropdownField(this.component);case"CHOICE":return this.renderChoiceField(this.component);case"SOCIAL":return this.renderSocialField(this.component);case"AUTH0_VERIFIABLE_CREDENTIALS":case"GMAPS_ADDRESS":case"RECAPTCHA":return console.warn(`Widget component "${this.component.type}" not yet implemented`),null;case"CARDS":case"CUSTOM":case"FILE":case"PAYMENT":return console.warn(`Component "${this.component.type}" not yet implemented`),null;default:return console.warn(`Unknown component type: ${this.component.type}`),null}}static get style(){return':host{display:block}.input-wrapper{display:flex;flex-direction:column;position:relative;margin-bottom:16px}.input-container{position:relative;width:100%}.input-label{position:absolute;left:16px;top:50%;transform:translateY(-50%);font-size:16px;font-weight:var(--ah-font-weight-label, 400);color:var(--ah-color-text-muted, #65676e);pointer-events:none;transition:all 0.15s ease-out;background-color:transparent;padding:0;z-index:1}.input-label.floating,.input-field:focus+.input-label,.input-field:not(:placeholder-shown)+.input-label{top:-8px;transform:translateY(0);font-size:12px;background-color:var(--ah-color-bg, #ffffff);padding:0 4px;left:12px;color:var(--ah-color-text-muted, #65676e)}.input-field:focus+.input-label{color:var(--ah-color-primary, #635dff)}.required{color:var(--ah-color-error, #d03c38);margin-left:2px}.input-field{width:100%;padding:16px;font-size:16px;font-family:inherit;color:var(--ah-color-text, #1e212a);background-color:var(--ah-color-input-bg, #ffffff);border:1px solid var(--ah-color-border, #c9cace);border-radius:var(--ah-input-radius, 3px);outline:none;transition:border-color 0.15s ease-out, box-shadow 0.15s ease-out;box-sizing:border-box}.input-field::placeholder{color:transparent}.input-field:hover{border-color:var(--ah-color-border-hover, #65676e)}.input-field:focus{border-color:var(--ah-color-primary, #635dff);box-shadow:inset 0 0 0 1px var(--ah-color-primary, #635dff)}.input-field.has-error{border-color:var(--ah-color-error, #d03c38)}.input-field.has-error:focus{box-shadow:inset 0 0 0 1px var(--ah-color-error, #d03c38)}.input-field:disabled{background-color:var(--ah-color-bg-disabled, #f5f5f5);border-color:var(--ah-color-border-disabled, #e0e1e3);cursor:not-allowed;opacity:0.7}.password-container{position:relative;display:flex;align-items:center}.password-container .input-field{padding-right:48px}.password-toggle{position:absolute;right:12px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;color:var(--ah-color-text-muted, #65676e);transition:color 0.15s ease}.password-toggle:hover{color:var(--ah-color-text, #1e212a)}.password-toggle svg{width:20px;height:20px}.error-text{font-size:12px;color:var(--ah-color-error, #d03c38);margin-top:4px;line-height:1.4}.helper-text{font-size:12px;color:var(--ah-color-text-muted, #65676e);margin-top:4px;line-height:1.4}.field-link{display:block;text-align:left;margin-top:8px;margin-bottom:16px}.field-link a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;font-weight:var(--ah-font-weight-link, 400)}.field-link a:hover{text-decoration:underline}.checkbox-wrapper{display:flex;align-items:flex-start;gap:10px;cursor:pointer;margin-bottom:16px}.checkbox-wrapper input[type="checkbox"]{width:18px;height:18px;margin:0;accent-color:var(--ah-color-primary, #635dff);cursor:pointer;flex-shrink:0}.checkbox-label{font-size:14px;color:var(--ah-color-text, #1e212a);line-height:1.5}.btn{display:inline-flex;align-items:center;justify-content:center;gap:10px;width:100%;padding:14px 20px;font-size:16px;font-weight:var(--ah-font-weight-btn, 400);font-family:inherit;line-height:1.25;text-align:center;text-decoration:none;border:none;border-radius:var(--ah-btn-radius, 3px);cursor:pointer;transition:background-color 0.15s ease, border-color 0.15s ease, transform 0.1s ease;box-sizing:border-box}.btn:disabled{opacity:0.6;cursor:not-allowed}.btn:not(:disabled):active{transform:scale(0.98)}.btn:focus-visible{outline:2px solid var(--ah-color-primary, #635dff);outline-offset:2px}.btn-primary{background-color:var(--ah-color-primary, #635dff);color:var(--ah-color-text-on-primary, #ffffff);margin-top:12px}.btn-primary:not(:disabled):hover{filter:brightness(0.85)}.btn-secondary{background-color:var(--ah-color-bg, #ffffff);color:var(--ah-color-text, #1e212a);border:1px solid var(--ah-color-border, #c9cace)}.btn-secondary:not(:disabled):hover{background-color:var(--ah-color-bg-hover, #f5f5f5);border-color:var(--ah-color-border-hover, #65676e)}.btn-link{background:none;border:none;color:var(--ah-color-link, #635dff);padding:8px 0;font-weight:var(--ah-font-weight-link, 400);text-decoration:none}.btn-link:hover{text-decoration:underline}.social-buttons{display:flex;flex-direction:column;gap:12px}.btn-social{display:flex;align-items:center;justify-content:center;gap:12px}.social-icon{width:20px;height:20px;flex-shrink:0}@media (max-width: 480px){.social-buttons:has(.btn-social:nth-child(3)){flex-direction:row;flex-wrap:nowrap;justify-content:stretch;gap:8px}.social-buttons:has(.btn-social:nth-child(3)) .btn-social{width:auto;min-width:0;padding:12px;flex:1 1 0}.social-buttons:has(.btn-social:nth-child(3)) .btn-social span{display:none}.social-buttons:has(.btn-social:nth-child(3)) .social-icon{width:24px;height:24px}}.btn-icon{width:20px;height:20px;flex-shrink:0}.btn-icon img{width:100%;height:100%;object-fit:contain}.text-title{font-size:20px;font-weight:400;color:var(--ah-color-text, #1e212a);margin:8px 0;line-height:1.3}.text-title.text-success{color:var(--ah-color-success, #13a769)}.text-description{font-size:14px;color:var(--ah-color-text-muted, #65676e);margin:4px 0;line-height:1.5}.image{display:block;max-width:100%;height:auto;border-radius:4px}.image-centered{margin:0 auto 16px;width:52px;height:52px;object-fit:contain}.link{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;transition:color 0.15s ease}.link:hover{text-decoration:underline}.link:focus-visible{outline:2px solid var(--ah-color-link, #635dff);outline-offset:2px;border-radius:2px}.node-error{padding:12px 16px;background-color:var(--ah-color-error-bg, #ffeaea);color:var(--ah-color-error, #d03c38);border-left:3px solid var(--ah-color-error, #d03c38);border-radius:0;font-size:14px;margin-bottom:16px}.node-success{padding:12px 16px;background-color:var(--ah-color-success-bg, #e6f9f1);color:var(--ah-color-success, #13a769);border-left:3px solid var(--ah-color-success, #13a769);border-radius:0;font-size:14px;margin-bottom:16px}.divider{display:flex;align-items:center;text-align:center;margin:16px 0}.divider::before,.divider::after{content:"";flex:1;border-bottom:1px solid var(--ah-color-border-muted, #c9cace)}.divider-text{padding:0 10px;font-size:12px;font-weight:400;color:var(--ah-color-text-muted, #65676e);text-transform:uppercase;letter-spacing:0.5px}'}},[513,"authhero-node",{component:[16],value:[1],disabled:[4],passwordVisible:[32]}]);function s(){"undefined"!=typeof customElements&&["authhero-node"].forEach((e=>{"authhero-node"===e&&(customElements.get(t(e))||customElements.define(t(e),a))}))}s();export{a as A,s as d}
|
|
@@ -44,6 +44,17 @@ const AuthheroNode = class {
|
|
|
44
44
|
value: target.checked ? "true" : "false",
|
|
45
45
|
});
|
|
46
46
|
};
|
|
47
|
+
/**
|
|
48
|
+
* Sanitize a string for use in CSS class names and part tokens.
|
|
49
|
+
* Replaces spaces and special characters with hyphens, converts to lowercase.
|
|
50
|
+
*/
|
|
51
|
+
sanitizeForCssToken(value) {
|
|
52
|
+
return value
|
|
53
|
+
.toLowerCase()
|
|
54
|
+
.replace(/[^a-z0-9-]/g, "-") // Replace non-alphanumeric chars with hyphen
|
|
55
|
+
.replace(/-+/g, "-") // Collapse multiple hyphens
|
|
56
|
+
.replace(/^-|-$/g, ""); // Remove leading/trailing hyphens
|
|
57
|
+
}
|
|
47
58
|
handleButtonClick = (e, type, value) => {
|
|
48
59
|
if (type !== "submit") {
|
|
49
60
|
e.preventDefault();
|
|
@@ -312,7 +323,10 @@ const AuthheroNode = class {
|
|
|
312
323
|
// Default: generic globe icon for unknown providers
|
|
313
324
|
return (h("svg", { class: "social-icon", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg" }, h("circle", { cx: "12", cy: "12", r: "10", fill: "none", stroke: "#666", "stroke-width": "2" }), h("path", { d: "M2 12h20M12 2c-2.5 2.5-4 5.5-4 10s1.5 7.5 4 10c2.5-2.5 4-5.5 4-10s-1.5-7.5-4-10z", fill: "none", stroke: "#666", "stroke-width": "2" })));
|
|
314
325
|
};
|
|
315
|
-
return (h("div", { class: "social-buttons", part: "social-buttons" }, providers.map((provider) =>
|
|
326
|
+
return (h("div", { class: "social-buttons", part: "social-buttons" }, providers.map((provider) => {
|
|
327
|
+
const safeProvider = this.sanitizeForCssToken(provider);
|
|
328
|
+
return (h("button", { type: "button", class: `btn btn-secondary btn-social btn-social-${safeProvider}`, part: `button button-secondary button-social button-social-${safeProvider}`, "data-provider": provider, disabled: this.disabled, onClick: (e) => this.handleButtonClick(e, "SOCIAL", provider), key: provider }, getProviderIcon(provider), h("span", { part: "button-social-text" }, `Continue with ${getProviderDisplayName(provider)}`)));
|
|
329
|
+
})));
|
|
316
330
|
}
|
|
317
331
|
// ===========================================================================
|
|
318
332
|
// Main Render
|
|
@@ -288,6 +288,139 @@ function applyCssVars(element, vars) {
|
|
|
288
288
|
});
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
+
/**
|
|
292
|
+
* Sanitize HTML to only allow safe formatting tags
|
|
293
|
+
*
|
|
294
|
+
* Allowed tags:
|
|
295
|
+
* - <br>, <br/> - Line breaks
|
|
296
|
+
* - <em>, <i> - Italic
|
|
297
|
+
* - <strong>, <b> - Bold
|
|
298
|
+
* - <u> - Underline
|
|
299
|
+
* - <span> - Generic inline container (for styling)
|
|
300
|
+
* - <a> - Links (href attribute only, with target="_blank" and rel="noopener")
|
|
301
|
+
*
|
|
302
|
+
* All other tags and attributes are stripped.
|
|
303
|
+
*/
|
|
304
|
+
// Allowed tags and their allowed attributes
|
|
305
|
+
const ALLOWED_TAGS = {
|
|
306
|
+
br: [],
|
|
307
|
+
em: [],
|
|
308
|
+
i: [],
|
|
309
|
+
strong: [],
|
|
310
|
+
b: [],
|
|
311
|
+
u: [],
|
|
312
|
+
span: ["class"],
|
|
313
|
+
a: ["href", "class"],
|
|
314
|
+
};
|
|
315
|
+
/**
|
|
316
|
+
* Sanitize HTML string to only allow safe formatting tags
|
|
317
|
+
*
|
|
318
|
+
* @param html - The HTML string to sanitize
|
|
319
|
+
* @returns Sanitized HTML string safe for innerHTML
|
|
320
|
+
*/
|
|
321
|
+
function sanitizeHtml(html) {
|
|
322
|
+
if (!html)
|
|
323
|
+
return "";
|
|
324
|
+
// If no < character present, return as-is (optimization)
|
|
325
|
+
// Must check for any < to prevent bypassing sanitization with malformed tags
|
|
326
|
+
// like "<img src=x onerror=..." which forgiving HTML parsers may still execute
|
|
327
|
+
if (!html.includes("<")) {
|
|
328
|
+
return html;
|
|
329
|
+
}
|
|
330
|
+
// Use a simple regex-based approach that's safe for our limited use case
|
|
331
|
+
// This avoids needing DOMParser which may not be available in all environments
|
|
332
|
+
let result = html;
|
|
333
|
+
// First, escape all HTML
|
|
334
|
+
result = result
|
|
335
|
+
.replace(/&/g, "&")
|
|
336
|
+
.replace(/</g, "<")
|
|
337
|
+
.replace(/>/g, ">")
|
|
338
|
+
.replace(/"/g, """)
|
|
339
|
+
.replace(/'/g, "'");
|
|
340
|
+
// Then selectively re-enable allowed tags
|
|
341
|
+
for (const [tag, allowedAttrs] of Object.entries(ALLOWED_TAGS)) {
|
|
342
|
+
// Self-closing tags (like <br> and <br/>)
|
|
343
|
+
if (tag === "br") {
|
|
344
|
+
result = result.replace(/<br\s*\/?>/gi, "<br>");
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
// Opening tags with optional attributes
|
|
348
|
+
const openingPattern = new RegExp(`<${tag}((?:\\s+[a-z-]+(?:="[^&]*"|='[^&]*')?)*)\\s*>`, "gi");
|
|
349
|
+
result = result.replace(openingPattern, (_match, attrsStr) => {
|
|
350
|
+
// Parse and filter attributes
|
|
351
|
+
const filteredAttrs = [];
|
|
352
|
+
if (attrsStr) {
|
|
353
|
+
// Unescape the attributes string for parsing
|
|
354
|
+
const unescapedAttrs = attrsStr
|
|
355
|
+
.replace(/"/g, '"')
|
|
356
|
+
.replace(/'/g, "'")
|
|
357
|
+
.replace(/&/g, "&")
|
|
358
|
+
.replace(/</g, "<")
|
|
359
|
+
.replace(/>/g, ">");
|
|
360
|
+
// Extract attributes
|
|
361
|
+
const attrPattern = /([a-z-]+)=["']([^"']*)["']/gi;
|
|
362
|
+
let attrMatch;
|
|
363
|
+
while ((attrMatch = attrPattern.exec(unescapedAttrs)) !== null) {
|
|
364
|
+
const [, attrName, attrValue] = attrMatch;
|
|
365
|
+
if (attrName && allowedAttrs.includes(attrName.toLowerCase())) {
|
|
366
|
+
// For href, validate it's a safe URL
|
|
367
|
+
if (attrName.toLowerCase() === "href") {
|
|
368
|
+
if (isSafeUrl(attrValue || "")) {
|
|
369
|
+
filteredAttrs.push(`${attrName}="${escapeAttr(attrValue || "")}"`);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
filteredAttrs.push(`${attrName}="${escapeAttr(attrValue || "")}"`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
// For <a> tags, always add security attributes
|
|
379
|
+
if (tag === "a") {
|
|
380
|
+
filteredAttrs.push('target="_blank"');
|
|
381
|
+
filteredAttrs.push('rel="noopener noreferrer"');
|
|
382
|
+
}
|
|
383
|
+
const attrsOutput = filteredAttrs.length
|
|
384
|
+
? " " + filteredAttrs.join(" ")
|
|
385
|
+
: "";
|
|
386
|
+
return `<${tag}${attrsOutput}>`;
|
|
387
|
+
});
|
|
388
|
+
// Closing tags
|
|
389
|
+
const closingPattern = new RegExp(`</${tag}>`, "gi");
|
|
390
|
+
result = result.replace(closingPattern, `</${tag}>`);
|
|
391
|
+
}
|
|
392
|
+
return result;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Check if a URL is safe (http, https, or relative)
|
|
396
|
+
*/
|
|
397
|
+
function isSafeUrl(url) {
|
|
398
|
+
if (!url)
|
|
399
|
+
return false;
|
|
400
|
+
// Allow relative URLs
|
|
401
|
+
if (url.startsWith("/") || url.startsWith("#") || url.startsWith("?")) {
|
|
402
|
+
return true;
|
|
403
|
+
}
|
|
404
|
+
// Allow http and https
|
|
405
|
+
try {
|
|
406
|
+
const parsed = new URL(url, "https://example.com");
|
|
407
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Escape attribute value
|
|
415
|
+
*/
|
|
416
|
+
function escapeAttr(value) {
|
|
417
|
+
return value
|
|
418
|
+
.replace(/&/g, "&")
|
|
419
|
+
.replace(/"/g, """)
|
|
420
|
+
.replace(/</g, "<")
|
|
421
|
+
.replace(/>/g, ">");
|
|
422
|
+
}
|
|
423
|
+
|
|
291
424
|
const authheroWidgetCss = () => `:host{display:block;font-family:var(--ah-font-family, 'ulp-font', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, sans-serif);font-size:var(--ah-font-size-base, 14px);line-height:var(--ah-line-height-base, 1.5);color:var(--ah-color-text, #1e212a);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.widget-container{max-width:var(--ah-widget-max-width, 400px);width:100%;margin:0 auto;background-color:var(--ah-color-bg, #ffffff);border-radius:var(--ah-widget-radius, 5px);box-shadow:var(--ah-widget-shadow, 0 4px 22px 0 rgba(0, 0, 0, 0.11));box-sizing:border-box}.widget-header{padding:var(--ah-header-padding, 40px 48px 24px)}.widget-body{padding:var(--ah-body-padding, 0 48px 40px)}.logo-wrapper{display:var(--ah-logo-display, flex);justify-content:var(--ah-logo-align, center);margin-bottom:8px}.logo{display:block;height:var(--ah-logo-height, 52px);max-width:100%;width:auto;object-fit:contain}.title{font-size:var(--ah-font-size-title, 24px);font-weight:var(--ah-font-weight-title, 700);text-align:var(--ah-title-align, center);margin:var(--ah-title-margin, 24px 0 8px);color:var(--ah-color-header, #1e212a);line-height:1.2}.description{font-size:var(--ah-font-size-description, 14px);text-align:var(--ah-title-align, center);margin:var(--ah-description-margin, 0 0 8px);color:var(--ah-color-text, #1e212a);line-height:1.5}.message{padding:12px 16px;border-radius:4px;margin-bottom:16px;font-size:14px;line-height:1.5}.message-error{background-color:var(--ah-color-error-bg, #ffeaea);color:var(--ah-color-error, #d03c38);border-left:3px solid var(--ah-color-error, #d03c38)}.message-success{background-color:var(--ah-color-success-bg, #e6f9f1);color:var(--ah-color-success, #13a769);border-left:3px solid var(--ah-color-success, #13a769)}form{display:flex;flex-direction:column}.form-content{display:flex;flex-direction:column}.social-section{display:flex;flex-direction:column;gap:8px;order:var(--ah-social-order, 2)}.fields-section{display:flex;flex-direction:column;order:var(--ah-fields-order, 0)}.divider{display:flex;align-items:center;text-align:center;margin:16px 0;order:var(--ah-divider-order, 1)}.divider::before,.divider::after{content:'';flex:1;border-bottom:1px solid var(--ah-color-border-muted, #c9cace)}.divider-text{padding:0 10px;font-size:12px;font-weight:400;color:var(--ah-color-text-muted, #65676e);text-transform:uppercase;letter-spacing:0}.links{display:flex;flex-direction:column;align-items:center;gap:8px;margin-top:16px}.link-wrapper{font-size:14px;color:var(--ah-color-text, #1e212a)}.link{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;font-weight:var(--ah-font-weight-link, 400);transition:color 150ms ease}.link:hover{text-decoration:underline}.link:focus-visible{outline:2px solid var(--ah-color-link, #635dff);outline-offset:2px;border-radius:2px}.loading-spinner{width:32px;height:32px;margin:24px auto;border:3px solid var(--ah-color-border-muted, #e0e1e3);border-top-color:var(--ah-color-primary, #635dff);border-radius:50%;animation:spin 0.8s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.error-message{text-align:center;color:var(--ah-color-error, #d03c38);padding:16px;font-size:14px}@media (max-width: 480px){:host{display:block;width:100%;min-height:100vh;background-color:var(--ah-color-bg, #ffffff)}.widget-container{box-shadow:none;border-radius:0;max-width:none;width:100%;margin:0}.widget-header{padding:24px 16px 16px}.widget-body{padding:0 16px 24px}}`;
|
|
292
425
|
|
|
293
426
|
const AuthheroWidget = class {
|
|
@@ -886,7 +1019,7 @@ const AuthheroWidget = class {
|
|
|
886
1019
|
const hasDivider = components.some((c) => this.isDividerComponent(c));
|
|
887
1020
|
// Get logo URL from theme.widget (takes precedence) or branding
|
|
888
1021
|
const logoUrl = this._theme?.widget?.logo_url || this._branding?.logo_url;
|
|
889
|
-
return (h("div", { class: "widget-container", part: "container" }, h("header", { class: "widget-header", part: "header" }, logoUrl && (h("div", { class: "logo-wrapper", part: "logo-wrapper" }, h("img", { class: "logo", part: "logo", src: logoUrl, alt: "Logo" }))), this._screen.title && (h("h1", { class: "title", part: "title"
|
|
1022
|
+
return (h("div", { class: "widget-container", part: "container" }, h("header", { class: "widget-header", part: "header" }, logoUrl && (h("div", { class: "logo-wrapper", part: "logo-wrapper" }, h("img", { class: "logo", part: "logo", src: logoUrl, alt: "Logo" }))), this._screen.title && (h("h1", { class: "title", part: "title", innerHTML: sanitizeHtml(this._screen.title) })), this._screen.description && (h("p", { class: "description", part: "description", innerHTML: sanitizeHtml(this._screen.description) }))), h("div", { class: "widget-body", part: "body" }, screenErrors.map((err) => (h("div", { class: "message message-error", part: "message message-error", key: err.id ?? err.text }, err.text))), screenSuccesses.map((msg) => (h("div", { class: "message message-success", part: "message message-success", key: msg.id ?? msg.text }, msg.text))), h("form", { onSubmit: this.handleSubmit, part: "form" }, h("div", { class: "form-content" }, socialComponents.length > 0 && (h("div", { class: "social-section", part: "social-section" }, socialComponents.map((component) => (h("authhero-node", { key: component.id, component: component, value: this.formData[component.id], onFieldChange: (e) => this.handleInputChange(e.detail.id, e.detail.value), onButtonClick: (e) => this.handleButtonClick(e.detail), disabled: this.loading }))))), socialComponents.length > 0 &&
|
|
890
1023
|
fieldComponents.length > 0 &&
|
|
891
1024
|
hasDivider && (h("div", { class: "divider", part: "divider" }, h("span", { class: "divider-text" }, "Or"))), h("div", { class: "fields-section", part: "fields-section" }, fieldComponents.map((component) => (h("authhero-node", { key: component.id, component: component, value: this.formData[component.id], onFieldChange: (e) => this.handleInputChange(e.detail.id, e.detail.value), onButtonClick: (e) => this.handleButtonClick(e.detail), disabled: this.loading })))))), this._screen.links && this._screen.links.length > 0 && (h("div", { class: "links", part: "links" }, this._screen.links.map((link) => (h("span", { class: "link-wrapper", part: "link-wrapper", key: link.id ?? link.href }, link.linkText ? (h("span", null, link.text, " ", h("a", { href: link.href, class: "link", part: "link", onClick: (e) => this.handleLinkClick(e, {
|
|
892
1025
|
id: link.id,
|
|
@@ -35,6 +35,11 @@ export declare class AuthheroNode {
|
|
|
35
35
|
}>;
|
|
36
36
|
private handleInput;
|
|
37
37
|
private handleCheckbox;
|
|
38
|
+
/**
|
|
39
|
+
* Sanitize a string for use in CSS class names and part tokens.
|
|
40
|
+
* Replaces spaces and special characters with hyphens, converts to lowercase.
|
|
41
|
+
*/
|
|
42
|
+
private sanitizeForCssToken;
|
|
38
43
|
private handleButtonClick;
|
|
39
44
|
private togglePasswordVisibility;
|
|
40
45
|
/**
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sanitize HTML to only allow safe formatting tags
|
|
3
|
+
*
|
|
4
|
+
* Allowed tags:
|
|
5
|
+
* - <br>, <br/> - Line breaks
|
|
6
|
+
* - <em>, <i> - Italic
|
|
7
|
+
* - <strong>, <b> - Bold
|
|
8
|
+
* - <u> - Underline
|
|
9
|
+
* - <span> - Generic inline container (for styling)
|
|
10
|
+
* - <a> - Links (href attribute only, with target="_blank" and rel="noopener")
|
|
11
|
+
*
|
|
12
|
+
* All other tags and attributes are stripped.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Sanitize HTML string to only allow safe formatting tags
|
|
16
|
+
*
|
|
17
|
+
* @param html - The HTML string to sanitize
|
|
18
|
+
* @returns Sanitized HTML string safe for innerHTML
|
|
19
|
+
*/
|
|
20
|
+
export declare function sanitizeHtml(html: string | undefined | null): string;
|
package/hydrate/index.js
CHANGED
|
@@ -5015,6 +5015,17 @@ class AuthheroNode {
|
|
|
5015
5015
|
value: target.checked ? "true" : "false",
|
|
5016
5016
|
});
|
|
5017
5017
|
};
|
|
5018
|
+
/**
|
|
5019
|
+
* Sanitize a string for use in CSS class names and part tokens.
|
|
5020
|
+
* Replaces spaces and special characters with hyphens, converts to lowercase.
|
|
5021
|
+
*/
|
|
5022
|
+
sanitizeForCssToken(value) {
|
|
5023
|
+
return value
|
|
5024
|
+
.toLowerCase()
|
|
5025
|
+
.replace(/[^a-z0-9-]/g, "-") // Replace non-alphanumeric chars with hyphen
|
|
5026
|
+
.replace(/-+/g, "-") // Collapse multiple hyphens
|
|
5027
|
+
.replace(/^-|-$/g, ""); // Remove leading/trailing hyphens
|
|
5028
|
+
}
|
|
5018
5029
|
handleButtonClick = (e, type, value) => {
|
|
5019
5030
|
if (type !== "submit") {
|
|
5020
5031
|
e.preventDefault();
|
|
@@ -5283,7 +5294,10 @@ class AuthheroNode {
|
|
|
5283
5294
|
// Default: generic globe icon for unknown providers
|
|
5284
5295
|
return (hAsync("svg", { class: "social-icon", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg" }, hAsync("circle", { cx: "12", cy: "12", r: "10", fill: "none", stroke: "#666", "stroke-width": "2" }), hAsync("path", { d: "M2 12h20M12 2c-2.5 2.5-4 5.5-4 10s1.5 7.5 4 10c2.5-2.5 4-5.5 4-10s-1.5-7.5-4-10z", fill: "none", stroke: "#666", "stroke-width": "2" })));
|
|
5285
5296
|
};
|
|
5286
|
-
return (hAsync("div", { class: "social-buttons", part: "social-buttons" }, providers.map((provider) =>
|
|
5297
|
+
return (hAsync("div", { class: "social-buttons", part: "social-buttons" }, providers.map((provider) => {
|
|
5298
|
+
const safeProvider = this.sanitizeForCssToken(provider);
|
|
5299
|
+
return (hAsync("button", { type: "button", class: `btn btn-secondary btn-social btn-social-${safeProvider}`, part: `button button-secondary button-social button-social-${safeProvider}`, "data-provider": provider, disabled: this.disabled, onClick: (e) => this.handleButtonClick(e, "SOCIAL", provider), key: provider }, getProviderIcon(provider), hAsync("span", { part: "button-social-text" }, `Continue with ${getProviderDisplayName(provider)}`)));
|
|
5300
|
+
})));
|
|
5287
5301
|
}
|
|
5288
5302
|
// ===========================================================================
|
|
5289
5303
|
// Main Render
|
|
@@ -5661,6 +5675,139 @@ function applyCssVars(element, vars) {
|
|
|
5661
5675
|
});
|
|
5662
5676
|
}
|
|
5663
5677
|
|
|
5678
|
+
/**
|
|
5679
|
+
* Sanitize HTML to only allow safe formatting tags
|
|
5680
|
+
*
|
|
5681
|
+
* Allowed tags:
|
|
5682
|
+
* - <br>, <br/> - Line breaks
|
|
5683
|
+
* - <em>, <i> - Italic
|
|
5684
|
+
* - <strong>, <b> - Bold
|
|
5685
|
+
* - <u> - Underline
|
|
5686
|
+
* - <span> - Generic inline container (for styling)
|
|
5687
|
+
* - <a> - Links (href attribute only, with target="_blank" and rel="noopener")
|
|
5688
|
+
*
|
|
5689
|
+
* All other tags and attributes are stripped.
|
|
5690
|
+
*/
|
|
5691
|
+
// Allowed tags and their allowed attributes
|
|
5692
|
+
const ALLOWED_TAGS = {
|
|
5693
|
+
br: [],
|
|
5694
|
+
em: [],
|
|
5695
|
+
i: [],
|
|
5696
|
+
strong: [],
|
|
5697
|
+
b: [],
|
|
5698
|
+
u: [],
|
|
5699
|
+
span: ["class"],
|
|
5700
|
+
a: ["href", "class"],
|
|
5701
|
+
};
|
|
5702
|
+
/**
|
|
5703
|
+
* Sanitize HTML string to only allow safe formatting tags
|
|
5704
|
+
*
|
|
5705
|
+
* @param html - The HTML string to sanitize
|
|
5706
|
+
* @returns Sanitized HTML string safe for innerHTML
|
|
5707
|
+
*/
|
|
5708
|
+
function sanitizeHtml(html) {
|
|
5709
|
+
if (!html)
|
|
5710
|
+
return "";
|
|
5711
|
+
// If no < character present, return as-is (optimization)
|
|
5712
|
+
// Must check for any < to prevent bypassing sanitization with malformed tags
|
|
5713
|
+
// like "<img src=x onerror=..." which forgiving HTML parsers may still execute
|
|
5714
|
+
if (!html.includes("<")) {
|
|
5715
|
+
return html;
|
|
5716
|
+
}
|
|
5717
|
+
// Use a simple regex-based approach that's safe for our limited use case
|
|
5718
|
+
// This avoids needing DOMParser which may not be available in all environments
|
|
5719
|
+
let result = html;
|
|
5720
|
+
// First, escape all HTML
|
|
5721
|
+
result = result
|
|
5722
|
+
.replace(/&/g, "&")
|
|
5723
|
+
.replace(/</g, "<")
|
|
5724
|
+
.replace(/>/g, ">")
|
|
5725
|
+
.replace(/"/g, """)
|
|
5726
|
+
.replace(/'/g, "'");
|
|
5727
|
+
// Then selectively re-enable allowed tags
|
|
5728
|
+
for (const [tag, allowedAttrs] of Object.entries(ALLOWED_TAGS)) {
|
|
5729
|
+
// Self-closing tags (like <br> and <br/>)
|
|
5730
|
+
if (tag === "br") {
|
|
5731
|
+
result = result.replace(/<br\s*\/?>/gi, "<br>");
|
|
5732
|
+
continue;
|
|
5733
|
+
}
|
|
5734
|
+
// Opening tags with optional attributes
|
|
5735
|
+
const openingPattern = new RegExp(`<${tag}((?:\\s+[a-z-]+(?:="[^&]*"|='[^&]*')?)*)\\s*>`, "gi");
|
|
5736
|
+
result = result.replace(openingPattern, (_match, attrsStr) => {
|
|
5737
|
+
// Parse and filter attributes
|
|
5738
|
+
const filteredAttrs = [];
|
|
5739
|
+
if (attrsStr) {
|
|
5740
|
+
// Unescape the attributes string for parsing
|
|
5741
|
+
const unescapedAttrs = attrsStr
|
|
5742
|
+
.replace(/"/g, '"')
|
|
5743
|
+
.replace(/'/g, "'")
|
|
5744
|
+
.replace(/&/g, "&")
|
|
5745
|
+
.replace(/</g, "<")
|
|
5746
|
+
.replace(/>/g, ">");
|
|
5747
|
+
// Extract attributes
|
|
5748
|
+
const attrPattern = /([a-z-]+)=["']([^"']*)["']/gi;
|
|
5749
|
+
let attrMatch;
|
|
5750
|
+
while ((attrMatch = attrPattern.exec(unescapedAttrs)) !== null) {
|
|
5751
|
+
const [, attrName, attrValue] = attrMatch;
|
|
5752
|
+
if (attrName && allowedAttrs.includes(attrName.toLowerCase())) {
|
|
5753
|
+
// For href, validate it's a safe URL
|
|
5754
|
+
if (attrName.toLowerCase() === "href") {
|
|
5755
|
+
if (isSafeUrl(attrValue || "")) {
|
|
5756
|
+
filteredAttrs.push(`${attrName}="${escapeAttr(attrValue || "")}"`);
|
|
5757
|
+
}
|
|
5758
|
+
}
|
|
5759
|
+
else {
|
|
5760
|
+
filteredAttrs.push(`${attrName}="${escapeAttr(attrValue || "")}"`);
|
|
5761
|
+
}
|
|
5762
|
+
}
|
|
5763
|
+
}
|
|
5764
|
+
}
|
|
5765
|
+
// For <a> tags, always add security attributes
|
|
5766
|
+
if (tag === "a") {
|
|
5767
|
+
filteredAttrs.push('target="_blank"');
|
|
5768
|
+
filteredAttrs.push('rel="noopener noreferrer"');
|
|
5769
|
+
}
|
|
5770
|
+
const attrsOutput = filteredAttrs.length
|
|
5771
|
+
? " " + filteredAttrs.join(" ")
|
|
5772
|
+
: "";
|
|
5773
|
+
return `<${tag}${attrsOutput}>`;
|
|
5774
|
+
});
|
|
5775
|
+
// Closing tags
|
|
5776
|
+
const closingPattern = new RegExp(`</${tag}>`, "gi");
|
|
5777
|
+
result = result.replace(closingPattern, `</${tag}>`);
|
|
5778
|
+
}
|
|
5779
|
+
return result;
|
|
5780
|
+
}
|
|
5781
|
+
/**
|
|
5782
|
+
* Check if a URL is safe (http, https, or relative)
|
|
5783
|
+
*/
|
|
5784
|
+
function isSafeUrl(url) {
|
|
5785
|
+
if (!url)
|
|
5786
|
+
return false;
|
|
5787
|
+
// Allow relative URLs
|
|
5788
|
+
if (url.startsWith("/") || url.startsWith("#") || url.startsWith("?")) {
|
|
5789
|
+
return true;
|
|
5790
|
+
}
|
|
5791
|
+
// Allow http and https
|
|
5792
|
+
try {
|
|
5793
|
+
const parsed = new URL(url, "https://example.com");
|
|
5794
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
5795
|
+
}
|
|
5796
|
+
catch {
|
|
5797
|
+
return false;
|
|
5798
|
+
}
|
|
5799
|
+
}
|
|
5800
|
+
/**
|
|
5801
|
+
* Escape attribute value
|
|
5802
|
+
*/
|
|
5803
|
+
function escapeAttr(value) {
|
|
5804
|
+
return value
|
|
5805
|
+
.replace(/&/g, "&")
|
|
5806
|
+
.replace(/"/g, """)
|
|
5807
|
+
.replace(/</g, "<")
|
|
5808
|
+
.replace(/>/g, ">");
|
|
5809
|
+
}
|
|
5810
|
+
|
|
5664
5811
|
const authheroWidgetCss = () => `:host{display:block;font-family:var(--ah-font-family, 'ulp-font', -apple-system, BlinkMacSystemFont, Roboto, Helvetica, sans-serif);font-size:var(--ah-font-size-base, 14px);line-height:var(--ah-line-height-base, 1.5);color:var(--ah-color-text, #1e212a);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.widget-container{max-width:var(--ah-widget-max-width, 400px);width:100%;margin:0 auto;background-color:var(--ah-color-bg, #ffffff);border-radius:var(--ah-widget-radius, 5px);box-shadow:var(--ah-widget-shadow, 0 4px 22px 0 rgba(0, 0, 0, 0.11));box-sizing:border-box}.widget-header{padding:var(--ah-header-padding, 40px 48px 24px)}.widget-body{padding:var(--ah-body-padding, 0 48px 40px)}.logo-wrapper{display:var(--ah-logo-display, flex);justify-content:var(--ah-logo-align, center);margin-bottom:8px}.logo{display:block;height:var(--ah-logo-height, 52px);max-width:100%;width:auto;object-fit:contain}.title{font-size:var(--ah-font-size-title, 24px);font-weight:var(--ah-font-weight-title, 700);text-align:var(--ah-title-align, center);margin:var(--ah-title-margin, 24px 0 8px);color:var(--ah-color-header, #1e212a);line-height:1.2}.description{font-size:var(--ah-font-size-description, 14px);text-align:var(--ah-title-align, center);margin:var(--ah-description-margin, 0 0 8px);color:var(--ah-color-text, #1e212a);line-height:1.5}.message{padding:12px 16px;border-radius:4px;margin-bottom:16px;font-size:14px;line-height:1.5}.message-error{background-color:var(--ah-color-error-bg, #ffeaea);color:var(--ah-color-error, #d03c38);border-left:3px solid var(--ah-color-error, #d03c38)}.message-success{background-color:var(--ah-color-success-bg, #e6f9f1);color:var(--ah-color-success, #13a769);border-left:3px solid var(--ah-color-success, #13a769)}form{display:flex;flex-direction:column}.form-content{display:flex;flex-direction:column}.social-section{display:flex;flex-direction:column;gap:8px;order:var(--ah-social-order, 2)}.fields-section{display:flex;flex-direction:column;order:var(--ah-fields-order, 0)}.divider{display:flex;align-items:center;text-align:center;margin:16px 0;order:var(--ah-divider-order, 1)}.divider::before,.divider::after{content:'';flex:1;border-bottom:1px solid var(--ah-color-border-muted, #c9cace)}.divider-text{padding:0 10px;font-size:12px;font-weight:400;color:var(--ah-color-text-muted, #65676e);text-transform:uppercase;letter-spacing:0}.links{display:flex;flex-direction:column;align-items:center;gap:8px;margin-top:16px}.link-wrapper{font-size:14px;color:var(--ah-color-text, #1e212a)}.link{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:14px;font-weight:var(--ah-font-weight-link, 400);transition:color 150ms ease}.link:hover{text-decoration:underline}.link:focus-visible{outline:2px solid var(--ah-color-link, #635dff);outline-offset:2px;border-radius:2px}.loading-spinner{width:32px;height:32px;margin:24px auto;border:3px solid var(--ah-color-border-muted, #e0e1e3);border-top-color:var(--ah-color-primary, #635dff);border-radius:50%;animation:spin 0.8s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.error-message{text-align:center;color:var(--ah-color-error, #d03c38);padding:16px;font-size:14px}@media (max-width: 480px){:host{display:block;width:100%;min-height:100vh;background-color:var(--ah-color-bg, #ffffff)}.widget-container{box-shadow:none;border-radius:0;max-width:none;width:100%;margin:0}.widget-header{padding:24px 16px 16px}.widget-body{padding:0 16px 24px}}`;
|
|
5665
5812
|
|
|
5666
5813
|
class AuthheroWidget {
|
|
@@ -6259,7 +6406,7 @@ class AuthheroWidget {
|
|
|
6259
6406
|
const hasDivider = components.some((c) => this.isDividerComponent(c));
|
|
6260
6407
|
// Get logo URL from theme.widget (takes precedence) or branding
|
|
6261
6408
|
const logoUrl = this._theme?.widget?.logo_url || this._branding?.logo_url;
|
|
6262
|
-
return (hAsync("div", { class: "widget-container", part: "container" }, hAsync("header", { class: "widget-header", part: "header" }, logoUrl && (hAsync("div", { class: "logo-wrapper", part: "logo-wrapper" }, hAsync("img", { class: "logo", part: "logo", src: logoUrl, alt: "Logo" }))), this._screen.title && (hAsync("h1", { class: "title", part: "title"
|
|
6409
|
+
return (hAsync("div", { class: "widget-container", part: "container" }, hAsync("header", { class: "widget-header", part: "header" }, logoUrl && (hAsync("div", { class: "logo-wrapper", part: "logo-wrapper" }, hAsync("img", { class: "logo", part: "logo", src: logoUrl, alt: "Logo" }))), this._screen.title && (hAsync("h1", { class: "title", part: "title", innerHTML: sanitizeHtml(this._screen.title) })), this._screen.description && (hAsync("p", { class: "description", part: "description", innerHTML: sanitizeHtml(this._screen.description) }))), hAsync("div", { class: "widget-body", part: "body" }, screenErrors.map((err) => (hAsync("div", { class: "message message-error", part: "message message-error", key: err.id ?? err.text }, err.text))), screenSuccesses.map((msg) => (hAsync("div", { class: "message message-success", part: "message message-success", key: msg.id ?? msg.text }, msg.text))), hAsync("form", { onSubmit: this.handleSubmit, part: "form" }, hAsync("div", { class: "form-content" }, socialComponents.length > 0 && (hAsync("div", { class: "social-section", part: "social-section" }, socialComponents.map((component) => (hAsync("authhero-node", { key: component.id, component: component, value: this.formData[component.id], onFieldChange: (e) => this.handleInputChange(e.detail.id, e.detail.value), onButtonClick: (e) => this.handleButtonClick(e.detail), disabled: this.loading }))))), socialComponents.length > 0 &&
|
|
6263
6410
|
fieldComponents.length > 0 &&
|
|
6264
6411
|
hasDivider && (hAsync("div", { class: "divider", part: "divider" }, hAsync("span", { class: "divider-text" }, "Or"))), hAsync("div", { class: "fields-section", part: "fields-section" }, fieldComponents.map((component) => (hAsync("authhero-node", { key: component.id, component: component, value: this.formData[component.id], onFieldChange: (e) => this.handleInputChange(e.detail.id, e.detail.value), onButtonClick: (e) => this.handleButtonClick(e.detail), disabled: this.loading })))))), this._screen.links && this._screen.links.length > 0 && (hAsync("div", { class: "links", part: "links" }, this._screen.links.map((link) => (hAsync("span", { class: "link-wrapper", part: "link-wrapper", key: link.id ?? link.href }, link.linkText ? (hAsync("span", null, link.text, " ", hAsync("a", { href: link.href, class: "link", part: "link", onClick: (e) => this.handleLinkClick(e, {
|
|
6265
6412
|
id: link.id,
|