@authhero/widget 0.28.1 → 0.29.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/index.esm.js +1 -1
- package/dist/authhero-widget/p-5428e2e1.entry.js +1 -0
- package/dist/cjs/authhero-widget.cjs.entry.js +206 -3
- package/dist/cjs/index.cjs.js +24 -6
- package/dist/collection/components/authhero-node/authhero-node.js +1 -1
- package/dist/collection/components/authhero-widget/authhero-widget.js +208 -5
- package/dist/components/authhero-widget.js +1 -1
- package/dist/components/index.js +1 -1
- package/dist/esm/authhero-widget.entry.js +206 -3
- package/dist/esm/index.js +24 -6
- package/dist/types/components/authhero-widget/authhero-widget.d.ts +22 -0
- package/hydrate/index.js +206 -3
- package/hydrate/index.mjs +206 -3
- package/package.json +2 -2
- package/dist/authhero-widget/p-b9ae0275.entry.js +0 -1
package/hydrate/index.js
CHANGED
|
@@ -6823,7 +6823,18 @@ class AuthheroWidget {
|
|
|
6823
6823
|
e.preventDefault();
|
|
6824
6824
|
if (!this._screen || this.loading)
|
|
6825
6825
|
return;
|
|
6826
|
-
|
|
6826
|
+
let submitData = { ...this.formData, ...(overrideData || {}) };
|
|
6827
|
+
// Merge hidden input values from DOM (may have been set programmatically
|
|
6828
|
+
// by inline scripts, e.g. passkey management buttons)
|
|
6829
|
+
const form = this.el.shadowRoot?.querySelector("form");
|
|
6830
|
+
if (form) {
|
|
6831
|
+
const hiddenInputs = form.querySelectorAll('input[type="hidden"]');
|
|
6832
|
+
hiddenInputs.forEach((input) => {
|
|
6833
|
+
if (input.name && input.value) {
|
|
6834
|
+
submitData[input.name] = input.value;
|
|
6835
|
+
}
|
|
6836
|
+
});
|
|
6837
|
+
}
|
|
6827
6838
|
// Always emit the submit event
|
|
6828
6839
|
this.formSubmit.emit({
|
|
6829
6840
|
screen: this._screen,
|
|
@@ -6899,6 +6910,10 @@ class AuthheroWidget {
|
|
|
6899
6910
|
this.state = result.state;
|
|
6900
6911
|
this.persistState();
|
|
6901
6912
|
}
|
|
6913
|
+
// Perform WebAuthn ceremony if present (structured data, not script)
|
|
6914
|
+
if (result.ceremony) {
|
|
6915
|
+
this.performWebAuthnCeremony(result.ceremony);
|
|
6916
|
+
}
|
|
6902
6917
|
// Focus first input on new screen
|
|
6903
6918
|
this.focusFirstInput();
|
|
6904
6919
|
}
|
|
@@ -6932,6 +6947,192 @@ class AuthheroWidget {
|
|
|
6932
6947
|
this.loading = false;
|
|
6933
6948
|
}
|
|
6934
6949
|
};
|
|
6950
|
+
/**
|
|
6951
|
+
* Override form.submit() so that scripts (e.g. WebAuthn ceremony) that call
|
|
6952
|
+
* form.submit() go through the widget's JSON fetch pipeline instead of a
|
|
6953
|
+
* native form-urlencoded POST.
|
|
6954
|
+
*/
|
|
6955
|
+
overrideFormSubmit() {
|
|
6956
|
+
const shadowRoot = this.el.shadowRoot;
|
|
6957
|
+
if (!shadowRoot)
|
|
6958
|
+
return;
|
|
6959
|
+
const form = shadowRoot.querySelector("form");
|
|
6960
|
+
if (!form)
|
|
6961
|
+
return;
|
|
6962
|
+
form.submit = () => {
|
|
6963
|
+
const formData = new FormData(form);
|
|
6964
|
+
const data = {};
|
|
6965
|
+
formData.forEach((value, key) => {
|
|
6966
|
+
if (typeof value === "string") {
|
|
6967
|
+
data[key] = value;
|
|
6968
|
+
}
|
|
6969
|
+
});
|
|
6970
|
+
const syntheticEvent = { preventDefault: () => { } };
|
|
6971
|
+
this.handleSubmit(syntheticEvent, data);
|
|
6972
|
+
};
|
|
6973
|
+
}
|
|
6974
|
+
/**
|
|
6975
|
+
* Validate and execute a structured WebAuthn ceremony returned by the server.
|
|
6976
|
+
* Instead of injecting arbitrary script content, this parses the ceremony JSON,
|
|
6977
|
+
* validates the expected fields, and calls the WebAuthn API natively.
|
|
6978
|
+
*/
|
|
6979
|
+
performWebAuthnCeremony(ceremony) {
|
|
6980
|
+
if (!this.isValidWebAuthnCeremony(ceremony)) {
|
|
6981
|
+
console.error("Invalid WebAuthn ceremony payload", ceremony);
|
|
6982
|
+
return;
|
|
6983
|
+
}
|
|
6984
|
+
requestAnimationFrame(() => {
|
|
6985
|
+
this.overrideFormSubmit();
|
|
6986
|
+
this.executeWebAuthnRegistration(ceremony);
|
|
6987
|
+
});
|
|
6988
|
+
}
|
|
6989
|
+
/**
|
|
6990
|
+
* Schema validation for WebAuthn ceremony payloads.
|
|
6991
|
+
* Checks required fields and types before invoking browser APIs.
|
|
6992
|
+
*/
|
|
6993
|
+
isValidWebAuthnCeremony(data) {
|
|
6994
|
+
if (typeof data !== "object" || data === null)
|
|
6995
|
+
return false;
|
|
6996
|
+
const obj = data;
|
|
6997
|
+
if (obj.type !== "webauthn-registration")
|
|
6998
|
+
return false;
|
|
6999
|
+
if (typeof obj.successAction !== "string")
|
|
7000
|
+
return false;
|
|
7001
|
+
const opts = obj.options;
|
|
7002
|
+
if (typeof opts !== "object" || opts === null)
|
|
7003
|
+
return false;
|
|
7004
|
+
const o = opts;
|
|
7005
|
+
if (typeof o.challenge !== "string")
|
|
7006
|
+
return false;
|
|
7007
|
+
const rp = o.rp;
|
|
7008
|
+
if (typeof rp !== "object" || rp === null)
|
|
7009
|
+
return false;
|
|
7010
|
+
if (typeof rp.id !== "string" ||
|
|
7011
|
+
typeof rp.name !== "string")
|
|
7012
|
+
return false;
|
|
7013
|
+
const user = o.user;
|
|
7014
|
+
if (typeof user !== "object" || user === null)
|
|
7015
|
+
return false;
|
|
7016
|
+
const u = user;
|
|
7017
|
+
if (typeof u.id !== "string" ||
|
|
7018
|
+
typeof u.name !== "string" ||
|
|
7019
|
+
typeof u.displayName !== "string")
|
|
7020
|
+
return false;
|
|
7021
|
+
if (!Array.isArray(o.pubKeyCredParams))
|
|
7022
|
+
return false;
|
|
7023
|
+
return true;
|
|
7024
|
+
}
|
|
7025
|
+
/**
|
|
7026
|
+
* Perform the WebAuthn navigator.credentials.create() ceremony and submit
|
|
7027
|
+
* the credential result via the form.
|
|
7028
|
+
*/
|
|
7029
|
+
async executeWebAuthnRegistration(ceremony) {
|
|
7030
|
+
const opts = ceremony.options;
|
|
7031
|
+
const b64uToBuf = (s) => {
|
|
7032
|
+
s = s.replace(/-/g, "+").replace(/_/g, "/");
|
|
7033
|
+
while (s.length % 4)
|
|
7034
|
+
s += "=";
|
|
7035
|
+
const b = atob(s);
|
|
7036
|
+
const a = new Uint8Array(b.length);
|
|
7037
|
+
for (let i = 0; i < b.length; i++)
|
|
7038
|
+
a[i] = b.charCodeAt(i);
|
|
7039
|
+
return a.buffer;
|
|
7040
|
+
};
|
|
7041
|
+
const bufToB64u = (b) => {
|
|
7042
|
+
const a = new Uint8Array(b);
|
|
7043
|
+
let s = "";
|
|
7044
|
+
for (let i = 0; i < a.length; i++)
|
|
7045
|
+
s += String.fromCharCode(a[i]);
|
|
7046
|
+
return btoa(s)
|
|
7047
|
+
.replace(/\+/g, "-")
|
|
7048
|
+
.replace(/\//g, "_")
|
|
7049
|
+
.replace(/=+$/, "");
|
|
7050
|
+
};
|
|
7051
|
+
const findForm = () => {
|
|
7052
|
+
const shadowRoot = this.el?.shadowRoot;
|
|
7053
|
+
if (shadowRoot) {
|
|
7054
|
+
const f = shadowRoot.querySelector("form");
|
|
7055
|
+
if (f)
|
|
7056
|
+
return f;
|
|
7057
|
+
}
|
|
7058
|
+
return document.querySelector("form");
|
|
7059
|
+
};
|
|
7060
|
+
try {
|
|
7061
|
+
const publicKey = {
|
|
7062
|
+
challenge: b64uToBuf(opts.challenge),
|
|
7063
|
+
rp: { id: opts.rp.id, name: opts.rp.name },
|
|
7064
|
+
user: {
|
|
7065
|
+
id: b64uToBuf(opts.user.id),
|
|
7066
|
+
name: opts.user.name,
|
|
7067
|
+
displayName: opts.user.displayName,
|
|
7068
|
+
},
|
|
7069
|
+
pubKeyCredParams: opts.pubKeyCredParams.map((p) => ({
|
|
7070
|
+
alg: p.alg,
|
|
7071
|
+
type: p.type,
|
|
7072
|
+
})),
|
|
7073
|
+
timeout: opts.timeout,
|
|
7074
|
+
attestation: (opts.attestation || "none"),
|
|
7075
|
+
authenticatorSelection: opts.authenticatorSelection
|
|
7076
|
+
? {
|
|
7077
|
+
residentKey: (opts.authenticatorSelection.residentKey ||
|
|
7078
|
+
"preferred"),
|
|
7079
|
+
userVerification: (opts.authenticatorSelection
|
|
7080
|
+
.userVerification ||
|
|
7081
|
+
"preferred"),
|
|
7082
|
+
}
|
|
7083
|
+
: undefined,
|
|
7084
|
+
};
|
|
7085
|
+
if (opts.excludeCredentials?.length) {
|
|
7086
|
+
publicKey.excludeCredentials = opts.excludeCredentials.map((c) => ({
|
|
7087
|
+
id: b64uToBuf(c.id),
|
|
7088
|
+
type: c.type,
|
|
7089
|
+
transports: (c.transports || []),
|
|
7090
|
+
}));
|
|
7091
|
+
}
|
|
7092
|
+
const cred = (await navigator.credentials.create({
|
|
7093
|
+
publicKey,
|
|
7094
|
+
}));
|
|
7095
|
+
const response = cred.response;
|
|
7096
|
+
const resp = {
|
|
7097
|
+
id: cred.id,
|
|
7098
|
+
rawId: bufToB64u(cred.rawId),
|
|
7099
|
+
type: cred.type,
|
|
7100
|
+
response: {
|
|
7101
|
+
attestationObject: bufToB64u(response.attestationObject),
|
|
7102
|
+
clientDataJSON: bufToB64u(response.clientDataJSON),
|
|
7103
|
+
},
|
|
7104
|
+
clientExtensionResults: cred.getClientExtensionResults(),
|
|
7105
|
+
authenticatorAttachment: cred.authenticatorAttachment || undefined,
|
|
7106
|
+
};
|
|
7107
|
+
if (typeof response.getTransports === "function") {
|
|
7108
|
+
resp.response.transports =
|
|
7109
|
+
response.getTransports();
|
|
7110
|
+
}
|
|
7111
|
+
const form = findForm();
|
|
7112
|
+
if (form) {
|
|
7113
|
+
const cf = form.querySelector('[name="credential-field"]') ||
|
|
7114
|
+
form.querySelector("#credential-field");
|
|
7115
|
+
const af = form.querySelector('[name="action-field"]') ||
|
|
7116
|
+
form.querySelector("#action-field");
|
|
7117
|
+
if (cf)
|
|
7118
|
+
cf.value = JSON.stringify(resp);
|
|
7119
|
+
if (af)
|
|
7120
|
+
af.value = ceremony.successAction;
|
|
7121
|
+
form.submit();
|
|
7122
|
+
}
|
|
7123
|
+
}
|
|
7124
|
+
catch (e) {
|
|
7125
|
+
console.error("WebAuthn registration error:", e);
|
|
7126
|
+
const form = findForm();
|
|
7127
|
+
if (form) {
|
|
7128
|
+
const af = form.querySelector('[name="action-field"]') ||
|
|
7129
|
+
form.querySelector("#action-field");
|
|
7130
|
+
if (af)
|
|
7131
|
+
af.value = "error";
|
|
7132
|
+
form.submit();
|
|
7133
|
+
}
|
|
7134
|
+
}
|
|
7135
|
+
}
|
|
6935
7136
|
handleButtonClick = (detail) => {
|
|
6936
7137
|
// If this is a submit button click, trigger form submission
|
|
6937
7138
|
if (detail.type === "submit") {
|
|
@@ -7134,9 +7335,11 @@ class AuthheroWidget {
|
|
|
7134
7335
|
// Use the local screen variable for all rendering
|
|
7135
7336
|
const screenErrors = screen.messages?.filter((m) => m.type === "error") || [];
|
|
7136
7337
|
const screenSuccesses = screen.messages?.filter((m) => m.type === "success") || [];
|
|
7137
|
-
const
|
|
7338
|
+
const allComponents = [...(screen.components ?? [])];
|
|
7339
|
+
const components = allComponents
|
|
7138
7340
|
.filter((c) => c.visible !== false)
|
|
7139
7341
|
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
7342
|
+
const hiddenComponents = allComponents.filter((c) => c.visible === false);
|
|
7140
7343
|
// Separate social, divider, and field components for layout ordering
|
|
7141
7344
|
const socialComponents = components.filter((c) => this.isSocialComponent(c));
|
|
7142
7345
|
const fieldComponents = components.filter((c) => !this.isSocialComponent(c) && !this.isDividerComponent(c));
|
|
@@ -7171,7 +7374,7 @@ class AuthheroWidget {
|
|
|
7171
7374
|
};
|
|
7172
7375
|
// Get logo URL from theme.widget (takes precedence) or branding
|
|
7173
7376
|
const logoUrl = this._theme?.widget?.logo_url || this._branding?.logo_url;
|
|
7174
|
-
return (hAsync("div", { class: "widget-container", part: "container", "data-authstack-container": true }, 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" }))), screen.title && (hAsync("h1", { class: "title", part: "title", innerHTML: sanitizeHtml(screen.title) })), screen.description && (hAsync("p", { class: "description", part: "description", innerHTML: sanitizeHtml(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, exportparts: getExportParts(component) }))))), socialComponents.length > 0 &&
|
|
7377
|
+
return (hAsync("div", { class: "widget-container", part: "container", "data-authstack-container": true }, 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" }))), screen.title && (hAsync("h1", { class: "title", part: "title", innerHTML: sanitizeHtml(screen.title) })), screen.description && (hAsync("p", { class: "description", part: "description", innerHTML: sanitizeHtml(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" }, hiddenComponents.map((c) => (hAsync("input", { type: "hidden", name: c.id, id: c.id, key: c.id, value: this.formData[c.id] || "" }))), 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, exportparts: getExportParts(component) }))))), socialComponents.length > 0 &&
|
|
7175
7378
|
fieldComponents.length > 0 &&
|
|
7176
7379
|
hasDivider && (hAsync("div", { class: "divider", part: "divider" }, hAsync("span", { class: "divider-text" }, dividerText))), 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 })))))), screen.links && screen.links.length > 0 && (hAsync("div", { class: "links", part: "links" }, 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, {
|
|
7177
7380
|
id: link.id,
|
package/hydrate/index.mjs
CHANGED
|
@@ -6821,7 +6821,18 @@ class AuthheroWidget {
|
|
|
6821
6821
|
e.preventDefault();
|
|
6822
6822
|
if (!this._screen || this.loading)
|
|
6823
6823
|
return;
|
|
6824
|
-
|
|
6824
|
+
let submitData = { ...this.formData, ...(overrideData || {}) };
|
|
6825
|
+
// Merge hidden input values from DOM (may have been set programmatically
|
|
6826
|
+
// by inline scripts, e.g. passkey management buttons)
|
|
6827
|
+
const form = this.el.shadowRoot?.querySelector("form");
|
|
6828
|
+
if (form) {
|
|
6829
|
+
const hiddenInputs = form.querySelectorAll('input[type="hidden"]');
|
|
6830
|
+
hiddenInputs.forEach((input) => {
|
|
6831
|
+
if (input.name && input.value) {
|
|
6832
|
+
submitData[input.name] = input.value;
|
|
6833
|
+
}
|
|
6834
|
+
});
|
|
6835
|
+
}
|
|
6825
6836
|
// Always emit the submit event
|
|
6826
6837
|
this.formSubmit.emit({
|
|
6827
6838
|
screen: this._screen,
|
|
@@ -6897,6 +6908,10 @@ class AuthheroWidget {
|
|
|
6897
6908
|
this.state = result.state;
|
|
6898
6909
|
this.persistState();
|
|
6899
6910
|
}
|
|
6911
|
+
// Perform WebAuthn ceremony if present (structured data, not script)
|
|
6912
|
+
if (result.ceremony) {
|
|
6913
|
+
this.performWebAuthnCeremony(result.ceremony);
|
|
6914
|
+
}
|
|
6900
6915
|
// Focus first input on new screen
|
|
6901
6916
|
this.focusFirstInput();
|
|
6902
6917
|
}
|
|
@@ -6930,6 +6945,192 @@ class AuthheroWidget {
|
|
|
6930
6945
|
this.loading = false;
|
|
6931
6946
|
}
|
|
6932
6947
|
};
|
|
6948
|
+
/**
|
|
6949
|
+
* Override form.submit() so that scripts (e.g. WebAuthn ceremony) that call
|
|
6950
|
+
* form.submit() go through the widget's JSON fetch pipeline instead of a
|
|
6951
|
+
* native form-urlencoded POST.
|
|
6952
|
+
*/
|
|
6953
|
+
overrideFormSubmit() {
|
|
6954
|
+
const shadowRoot = this.el.shadowRoot;
|
|
6955
|
+
if (!shadowRoot)
|
|
6956
|
+
return;
|
|
6957
|
+
const form = shadowRoot.querySelector("form");
|
|
6958
|
+
if (!form)
|
|
6959
|
+
return;
|
|
6960
|
+
form.submit = () => {
|
|
6961
|
+
const formData = new FormData(form);
|
|
6962
|
+
const data = {};
|
|
6963
|
+
formData.forEach((value, key) => {
|
|
6964
|
+
if (typeof value === "string") {
|
|
6965
|
+
data[key] = value;
|
|
6966
|
+
}
|
|
6967
|
+
});
|
|
6968
|
+
const syntheticEvent = { preventDefault: () => { } };
|
|
6969
|
+
this.handleSubmit(syntheticEvent, data);
|
|
6970
|
+
};
|
|
6971
|
+
}
|
|
6972
|
+
/**
|
|
6973
|
+
* Validate and execute a structured WebAuthn ceremony returned by the server.
|
|
6974
|
+
* Instead of injecting arbitrary script content, this parses the ceremony JSON,
|
|
6975
|
+
* validates the expected fields, and calls the WebAuthn API natively.
|
|
6976
|
+
*/
|
|
6977
|
+
performWebAuthnCeremony(ceremony) {
|
|
6978
|
+
if (!this.isValidWebAuthnCeremony(ceremony)) {
|
|
6979
|
+
console.error("Invalid WebAuthn ceremony payload", ceremony);
|
|
6980
|
+
return;
|
|
6981
|
+
}
|
|
6982
|
+
requestAnimationFrame(() => {
|
|
6983
|
+
this.overrideFormSubmit();
|
|
6984
|
+
this.executeWebAuthnRegistration(ceremony);
|
|
6985
|
+
});
|
|
6986
|
+
}
|
|
6987
|
+
/**
|
|
6988
|
+
* Schema validation for WebAuthn ceremony payloads.
|
|
6989
|
+
* Checks required fields and types before invoking browser APIs.
|
|
6990
|
+
*/
|
|
6991
|
+
isValidWebAuthnCeremony(data) {
|
|
6992
|
+
if (typeof data !== "object" || data === null)
|
|
6993
|
+
return false;
|
|
6994
|
+
const obj = data;
|
|
6995
|
+
if (obj.type !== "webauthn-registration")
|
|
6996
|
+
return false;
|
|
6997
|
+
if (typeof obj.successAction !== "string")
|
|
6998
|
+
return false;
|
|
6999
|
+
const opts = obj.options;
|
|
7000
|
+
if (typeof opts !== "object" || opts === null)
|
|
7001
|
+
return false;
|
|
7002
|
+
const o = opts;
|
|
7003
|
+
if (typeof o.challenge !== "string")
|
|
7004
|
+
return false;
|
|
7005
|
+
const rp = o.rp;
|
|
7006
|
+
if (typeof rp !== "object" || rp === null)
|
|
7007
|
+
return false;
|
|
7008
|
+
if (typeof rp.id !== "string" ||
|
|
7009
|
+
typeof rp.name !== "string")
|
|
7010
|
+
return false;
|
|
7011
|
+
const user = o.user;
|
|
7012
|
+
if (typeof user !== "object" || user === null)
|
|
7013
|
+
return false;
|
|
7014
|
+
const u = user;
|
|
7015
|
+
if (typeof u.id !== "string" ||
|
|
7016
|
+
typeof u.name !== "string" ||
|
|
7017
|
+
typeof u.displayName !== "string")
|
|
7018
|
+
return false;
|
|
7019
|
+
if (!Array.isArray(o.pubKeyCredParams))
|
|
7020
|
+
return false;
|
|
7021
|
+
return true;
|
|
7022
|
+
}
|
|
7023
|
+
/**
|
|
7024
|
+
* Perform the WebAuthn navigator.credentials.create() ceremony and submit
|
|
7025
|
+
* the credential result via the form.
|
|
7026
|
+
*/
|
|
7027
|
+
async executeWebAuthnRegistration(ceremony) {
|
|
7028
|
+
const opts = ceremony.options;
|
|
7029
|
+
const b64uToBuf = (s) => {
|
|
7030
|
+
s = s.replace(/-/g, "+").replace(/_/g, "/");
|
|
7031
|
+
while (s.length % 4)
|
|
7032
|
+
s += "=";
|
|
7033
|
+
const b = atob(s);
|
|
7034
|
+
const a = new Uint8Array(b.length);
|
|
7035
|
+
for (let i = 0; i < b.length; i++)
|
|
7036
|
+
a[i] = b.charCodeAt(i);
|
|
7037
|
+
return a.buffer;
|
|
7038
|
+
};
|
|
7039
|
+
const bufToB64u = (b) => {
|
|
7040
|
+
const a = new Uint8Array(b);
|
|
7041
|
+
let s = "";
|
|
7042
|
+
for (let i = 0; i < a.length; i++)
|
|
7043
|
+
s += String.fromCharCode(a[i]);
|
|
7044
|
+
return btoa(s)
|
|
7045
|
+
.replace(/\+/g, "-")
|
|
7046
|
+
.replace(/\//g, "_")
|
|
7047
|
+
.replace(/=+$/, "");
|
|
7048
|
+
};
|
|
7049
|
+
const findForm = () => {
|
|
7050
|
+
const shadowRoot = this.el?.shadowRoot;
|
|
7051
|
+
if (shadowRoot) {
|
|
7052
|
+
const f = shadowRoot.querySelector("form");
|
|
7053
|
+
if (f)
|
|
7054
|
+
return f;
|
|
7055
|
+
}
|
|
7056
|
+
return document.querySelector("form");
|
|
7057
|
+
};
|
|
7058
|
+
try {
|
|
7059
|
+
const publicKey = {
|
|
7060
|
+
challenge: b64uToBuf(opts.challenge),
|
|
7061
|
+
rp: { id: opts.rp.id, name: opts.rp.name },
|
|
7062
|
+
user: {
|
|
7063
|
+
id: b64uToBuf(opts.user.id),
|
|
7064
|
+
name: opts.user.name,
|
|
7065
|
+
displayName: opts.user.displayName,
|
|
7066
|
+
},
|
|
7067
|
+
pubKeyCredParams: opts.pubKeyCredParams.map((p) => ({
|
|
7068
|
+
alg: p.alg,
|
|
7069
|
+
type: p.type,
|
|
7070
|
+
})),
|
|
7071
|
+
timeout: opts.timeout,
|
|
7072
|
+
attestation: (opts.attestation || "none"),
|
|
7073
|
+
authenticatorSelection: opts.authenticatorSelection
|
|
7074
|
+
? {
|
|
7075
|
+
residentKey: (opts.authenticatorSelection.residentKey ||
|
|
7076
|
+
"preferred"),
|
|
7077
|
+
userVerification: (opts.authenticatorSelection
|
|
7078
|
+
.userVerification ||
|
|
7079
|
+
"preferred"),
|
|
7080
|
+
}
|
|
7081
|
+
: undefined,
|
|
7082
|
+
};
|
|
7083
|
+
if (opts.excludeCredentials?.length) {
|
|
7084
|
+
publicKey.excludeCredentials = opts.excludeCredentials.map((c) => ({
|
|
7085
|
+
id: b64uToBuf(c.id),
|
|
7086
|
+
type: c.type,
|
|
7087
|
+
transports: (c.transports || []),
|
|
7088
|
+
}));
|
|
7089
|
+
}
|
|
7090
|
+
const cred = (await navigator.credentials.create({
|
|
7091
|
+
publicKey,
|
|
7092
|
+
}));
|
|
7093
|
+
const response = cred.response;
|
|
7094
|
+
const resp = {
|
|
7095
|
+
id: cred.id,
|
|
7096
|
+
rawId: bufToB64u(cred.rawId),
|
|
7097
|
+
type: cred.type,
|
|
7098
|
+
response: {
|
|
7099
|
+
attestationObject: bufToB64u(response.attestationObject),
|
|
7100
|
+
clientDataJSON: bufToB64u(response.clientDataJSON),
|
|
7101
|
+
},
|
|
7102
|
+
clientExtensionResults: cred.getClientExtensionResults(),
|
|
7103
|
+
authenticatorAttachment: cred.authenticatorAttachment || undefined,
|
|
7104
|
+
};
|
|
7105
|
+
if (typeof response.getTransports === "function") {
|
|
7106
|
+
resp.response.transports =
|
|
7107
|
+
response.getTransports();
|
|
7108
|
+
}
|
|
7109
|
+
const form = findForm();
|
|
7110
|
+
if (form) {
|
|
7111
|
+
const cf = form.querySelector('[name="credential-field"]') ||
|
|
7112
|
+
form.querySelector("#credential-field");
|
|
7113
|
+
const af = form.querySelector('[name="action-field"]') ||
|
|
7114
|
+
form.querySelector("#action-field");
|
|
7115
|
+
if (cf)
|
|
7116
|
+
cf.value = JSON.stringify(resp);
|
|
7117
|
+
if (af)
|
|
7118
|
+
af.value = ceremony.successAction;
|
|
7119
|
+
form.submit();
|
|
7120
|
+
}
|
|
7121
|
+
}
|
|
7122
|
+
catch (e) {
|
|
7123
|
+
console.error("WebAuthn registration error:", e);
|
|
7124
|
+
const form = findForm();
|
|
7125
|
+
if (form) {
|
|
7126
|
+
const af = form.querySelector('[name="action-field"]') ||
|
|
7127
|
+
form.querySelector("#action-field");
|
|
7128
|
+
if (af)
|
|
7129
|
+
af.value = "error";
|
|
7130
|
+
form.submit();
|
|
7131
|
+
}
|
|
7132
|
+
}
|
|
7133
|
+
}
|
|
6933
7134
|
handleButtonClick = (detail) => {
|
|
6934
7135
|
// If this is a submit button click, trigger form submission
|
|
6935
7136
|
if (detail.type === "submit") {
|
|
@@ -7132,9 +7333,11 @@ class AuthheroWidget {
|
|
|
7132
7333
|
// Use the local screen variable for all rendering
|
|
7133
7334
|
const screenErrors = screen.messages?.filter((m) => m.type === "error") || [];
|
|
7134
7335
|
const screenSuccesses = screen.messages?.filter((m) => m.type === "success") || [];
|
|
7135
|
-
const
|
|
7336
|
+
const allComponents = [...(screen.components ?? [])];
|
|
7337
|
+
const components = allComponents
|
|
7136
7338
|
.filter((c) => c.visible !== false)
|
|
7137
7339
|
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
7340
|
+
const hiddenComponents = allComponents.filter((c) => c.visible === false);
|
|
7138
7341
|
// Separate social, divider, and field components for layout ordering
|
|
7139
7342
|
const socialComponents = components.filter((c) => this.isSocialComponent(c));
|
|
7140
7343
|
const fieldComponents = components.filter((c) => !this.isSocialComponent(c) && !this.isDividerComponent(c));
|
|
@@ -7169,7 +7372,7 @@ class AuthheroWidget {
|
|
|
7169
7372
|
};
|
|
7170
7373
|
// Get logo URL from theme.widget (takes precedence) or branding
|
|
7171
7374
|
const logoUrl = this._theme?.widget?.logo_url || this._branding?.logo_url;
|
|
7172
|
-
return (hAsync("div", { class: "widget-container", part: "container", "data-authstack-container": true }, 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" }))), screen.title && (hAsync("h1", { class: "title", part: "title", innerHTML: sanitizeHtml(screen.title) })), screen.description && (hAsync("p", { class: "description", part: "description", innerHTML: sanitizeHtml(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, exportparts: getExportParts(component) }))))), socialComponents.length > 0 &&
|
|
7375
|
+
return (hAsync("div", { class: "widget-container", part: "container", "data-authstack-container": true }, 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" }))), screen.title && (hAsync("h1", { class: "title", part: "title", innerHTML: sanitizeHtml(screen.title) })), screen.description && (hAsync("p", { class: "description", part: "description", innerHTML: sanitizeHtml(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" }, hiddenComponents.map((c) => (hAsync("input", { type: "hidden", name: c.id, id: c.id, key: c.id, value: this.formData[c.id] || "" }))), 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, exportparts: getExportParts(component) }))))), socialComponents.length > 0 &&
|
|
7173
7376
|
fieldComponents.length > 0 &&
|
|
7174
7377
|
hasDivider && (hAsync("div", { class: "divider", part: "divider" }, hAsync("span", { class: "divider-text" }, dividerText))), 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 })))))), screen.links && screen.links.length > 0 && (hAsync("div", { class: "links", part: "links" }, 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, {
|
|
7175
7378
|
id: link.id,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@authhero/widget",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "Server-Driven UI widget for AuthHero authentication flows",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"country-list": "^2.4.1",
|
|
41
|
-
"@authhero/adapter-interfaces": "0.
|
|
41
|
+
"@authhero/adapter-interfaces": "0.152.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@hono/node-server": "^1.14.1",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as t,c as e,g as i,h as s}from"./p-BFP_5sHV.js";function o(t){const e=t.match(/^#([0-9a-f]{3})$/i)||t.match(/^#([0-9a-f]{6})$/i);if(!e)return null;let i=e[1];3===i.length&&(i=i[0]+i[0]+i[1]+i[1]+i[2]+i[2]);const s=parseInt(i,16);return[s>>16&255,s>>8&255,255&s]}function n(t){const e=o(t);if(!e)return NaN;const[i,s,n]=e.map((t=>{const e=t/255;return e<=.04045?e/12.92:Math.pow((e+.055)/1.055,2.4)}));return.2126*i+.7152*s+.0722*n}function r(t,e){const i=n(t),s=n(e);return isNaN(i)||isNaN(s)?NaN:(Math.max(i,s)+.05)/(Math.min(i,s)+.05)}function a(t,e="light"){const i=r(t,"#000000"),s=r(t,"#ffffff");return"light"===e?i>1.35*s?"#000000":"#ffffff":1.35*i>s?"#000000":"#ffffff"}function c(t,e){const i=o(t);if(!i)return t;const[s,n,r]=i,a=t=>Math.max(0,Math.round(t*(1-e))).toString(16).padStart(2,"0");return`#${a(s)}${a(n)}${a(r)}`}function h(t,e){const i=o(t);if(!i)return t;const[s,n,r]=i,a=t=>Math.min(255,Math.round(t+(255-t)*e)).toString(16).padStart(2,"0");return`#${a(s)}${a(n)}${a(r)}`}function l(t,e,i=4.5){if(r(t,e)>=i)return t;const s=n(e)>.5;let o=t;for(let n=1;n<=10;n++)if(o=s?c(t,.1*n):h(t,.1*n),r(o,e)>=i)return o;return s?"#000000":"#ffffff"}function d(t,e){if(void 0!==e)return`${e}px`;switch(t){case"pill":return"9999px";case"rounded":return"8px";case"sharp":return"0";default:return}}function f(t){if(!t)return{};const e={};if(t.colors?.primary&&(e["--ah-color-primary"]=t.colors.primary,e["--ah-color-primary-hover"]=t.colors.primary),t.colors?.page_background){const i=t.colors.page_background;"solid"===i.type&&i.start?e["--ah-page-bg"]=i.start:"gradient"===i.type&&i.start&&i.end&&(e["--ah-page-bg"]=`linear-gradient(${i.angle_deg??180}deg, ${i.start}, ${i.end})`)}return t.logo_url&&(e["--ah-logo-url"]=`url(${t.logo_url})`),t.font?.url&&(e["--ah-font-url"]=t.font.url),e}function p(t){if(!t)return{};const e={};if(t.borders){const i=t.borders;void 0!==i.widget_corner_radius&&(e["--ah-widget-radius"]=`${i.widget_corner_radius}px`),void 0!==i.widget_border_weight&&(e["--ah-widget-border-width"]=`${i.widget_border_weight}px`),!1===i.show_widget_shadow&&(e["--ah-widget-shadow"]="none");const s=d(i.buttons_style,i.button_border_radius);s&&(e["--ah-btn-radius"]=s),void 0!==i.button_border_weight&&(e["--ah-btn-border-width"]=`${i.button_border_weight}px`);const o=d(i.inputs_style,i.input_border_radius);o&&(e["--ah-input-radius"]=o),void 0!==i.input_border_weight&&(e["--ah-input-border-width"]=`${i.input_border_weight}px`)}if(t.colors){const i=t.colors;if(i.primary_button)if(e["--ah-color-primary"]=i.primary_button,e["--ah-color-primary-hover"]=i.primary_button,i.primary_button_label&&r(i.primary_button_label,i.primary_button)>=4.5)e["--ah-color-text-on-primary"]=i.primary_button_label;else{e["--ah-color-text-on-primary"]=a(i.primary_button,"light");const t=a(i.primary_button,"dark");t!==e["--ah-color-text-on-primary"]&&(e["--ah-color-text-on-primary-dark"]=t)}else i.primary_button_label&&(e["--ah-color-text-on-primary"]=i.primary_button_label);i.secondary_button_border&&(e["--ah-btn-secondary-border"]=i.secondary_button_border),i.secondary_button_label&&(e["--ah-btn-secondary-text"]=i.secondary_button_label),i.body_text&&(e["--ah-color-text"]=i.body_text),i.header&&(e["--ah-color-text-header"]=i.header),i.input_labels_placeholders&&(e["--ah-color-text-label"]=i.input_labels_placeholders,e["--ah-color-text-muted"]=i.input_labels_placeholders),i.input_filled_text&&(e["--ah-color-input-text"]=i.input_filled_text),i.widget_background&&(e["--ah-color-bg"]=i.widget_background),i.input_background&&(e["--ah-color-input-bg"]=i.input_background),i.widget_border&&(e["--ah-widget-border-color"]=i.widget_border),i.input_border&&(e["--ah-color-border"]=i.input_border),i.links_focused_components&&(e["--ah-color-link"]=l(i.links_focused_components,i.widget_background||"#ffffff")),i.base_focus_color&&(e["--ah-color-focus-ring"]=i.base_focus_color),i.base_hover_color&&(e["--ah-color-primary-hover"]=i.base_hover_color),i.error&&(e["--ah-color-error"]=i.error),i.success&&(e["--ah-color-success"]=i.success),i.icons&&(e["--ah-color-icon"]=i.icons);const s=i.widget_background||"#ffffff",o=i.input_background||s,n=e["--ah-color-border"]||i.input_border||"#c9cace",c=r(n,s),h=r(n,o);Math.min(c,h)<3&&(e["--ah-color-border"]=l(n,c<h?s:o,3))}if(t.fonts){const i=t.fonts,s=i.reference_text_size||16,o=t=>t>=50?Math.round(t/100*s):t;i.font_url&&(e["--ah-font-url"]=i.font_url),i.reference_text_size&&(e["--ah-font-size-base"]=`${i.reference_text_size}px`),i.title?.size&&(e["--ah-font-size-title"]=`${o(i.title.size)}px`),i.subtitle?.size&&(e["--ah-font-size-subtitle"]=`${o(i.subtitle.size)}px`),i.body_text?.size&&(e["--ah-font-size-body"]=`${o(i.body_text.size)}px`),i.input_labels?.size&&(e["--ah-font-size-label"]=`${o(i.input_labels.size)}px`),i.buttons_text?.size&&(e["--ah-font-size-btn"]=`${o(i.buttons_text.size)}px`),i.links?.size&&(e["--ah-font-size-link"]=`${o(i.links.size)}px`),"underlined"===i.links_style&&(e["--ah-link-decoration"]="underline"),void 0!==i.title?.bold&&(e["--ah-font-weight-title"]=i.title.bold?"700":"400"),void 0!==i.subtitle?.bold&&(e["--ah-font-weight-subtitle"]=i.subtitle.bold?"700":"400"),void 0!==i.body_text?.bold&&(e["--ah-font-weight-body"]=i.body_text.bold?"700":"400"),void 0!==i.input_labels?.bold&&(e["--ah-font-weight-label"]=i.input_labels.bold?"700":"400"),void 0!==i.buttons_text?.bold&&(e["--ah-font-weight-btn"]=i.buttons_text.bold?"600":"400"),void 0!==i.links?.bold&&(e["--ah-font-weight-link"]=i.links.bold?"700":"400")}if(t.widget){const i=t.widget;if(i.header_text_alignment&&(e["--ah-title-align"]=i.header_text_alignment),i.logo_height&&(e["--ah-logo-height"]=`${i.logo_height}px`),i.logo_position){const t={center:"center",left:"flex-start",right:"flex-end"};"none"===i.logo_position?e["--ah-logo-display"]="none":e["--ah-logo-align"]=t[i.logo_position]??"center"}i.social_buttons_layout&&("top"===i.social_buttons_layout?(e["--ah-social-order"]="0",e["--ah-divider-order"]="1",e["--ah-fields-order"]="2"):(e["--ah-social-order"]="2",e["--ah-divider-order"]="1",e["--ah-fields-order"]="0"))}if(t.page_background){const i=t.page_background;i.background_color&&(e["--ah-page-bg"]=i.background_color),i.background_image_url&&(e["--ah-page-bg-image"]=`url(${i.background_image_url})`)}return e}const u={br:[],em:[],i:[],strong:[],b:[],u:[],span:["class"],a:["href","class"]};function g(t){if(!t)return"";if(!t.includes("<"))return t;let e=t;e=e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'");for(const[t,i]of Object.entries(u)){if("br"===t){e=e.replace(/<br\s*\/?>/gi,"<br>");continue}const s=new RegExp(`<${t}((?:\\s+[a-z-]+(?:="[^&]*"|='[^&]*')?)*)\\s*>`,"gi");e=e.replace(s,((e,s)=>{const o=[];if(s){const t=s.replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"),e=/([a-z-]+)=["']([^"']*)["']/gi;let n;for(;null!==(n=e.exec(t));){const[,t,e]=n;t&&i.includes(t.toLowerCase())&&("href"===t.toLowerCase()?m(e||"")&&o.push(`${t}="${x(e||"")}"`):o.push(`${t}="${x(e||"")}"`))}}"a"===t&&(o.push('target="_blank"'),o.push('rel="noopener noreferrer"'));const n=o.length?" "+o.join(" "):"";return`<${t}${n}>`}));const o=new RegExp(`</${t}>`,"gi");e=e.replace(o,`</${t}>`)}return e}function m(t){if(!t)return!1;if(t.startsWith("/")||t.startsWith("#")||t.startsWith("?"))return!0;try{const e=new URL(t,"https://example.com");return"http:"===e.protocol||"https:"===e.protocol}catch{return!1}}function x(t){return t.replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}const w=class{constructor(i){t(this,i),this.formSubmit=e(this,"formSubmit"),this.buttonClick=e(this,"buttonClick"),this.linkClick=e(this,"linkClick"),this.navigate=e(this,"navigate"),this.flowComplete=e(this,"flowComplete"),this.flowError=e(this,"flowError"),this.screenChange=e(this,"screenChange")}get el(){return i(this)}screen;apiUrl;baseUrl;state;screenId;watchScreenId(){this.updateDataScreenAttribute()}authParams;statePersistence="memory";storageKey="authhero_widget";branding;theme;loading=!1;autoSubmit=!1;autoNavigate;_screen;_authParams;_branding;_theme;formData={};formSubmit;buttonClick;linkClick;navigate;flowComplete;flowError;screenChange;watchScreen(t){if("string"==typeof t)try{this._screen=JSON.parse(t)}catch{console.error("Failed to parse screen JSON")}else this._screen=t;this._screen&&(this.formData={},this.initFormDataFromDefaults(this._screen),this.screenChange.emit(this._screen),this.updateDataScreenAttribute())}initFormDataFromDefaults(t){const e={};for(const i of t.components||[])if("config"in i&&i.config&&"default_value"in i.config&&i.config.default_value){const t=i.config.default_value;"string"==typeof t&&""!==t&&(e[i.id]=t)}Object.keys(e).length>0&&(this.formData={...e,...this.formData})}updateDataScreenAttribute(){const t=this._screen?.name||this.screenId;t?this.el.setAttribute("data-screen",t):this.el.removeAttribute("data-screen");const e=this.el.closest("[data-authhero-widget-container]");e&&(t?e.setAttribute("data-screen",t):e.removeAttribute("data-screen"))}watchBranding(t){if("string"==typeof t)try{this._branding=JSON.parse(t)}catch{console.error("Failed to parse branding JSON")}else this._branding=t;this.applyThemeStyles()}watchTheme(t){if("string"==typeof t)try{this._theme=JSON.parse(t)}catch{console.error("Failed to parse theme JSON")}else this._theme=t;this.applyThemeStyles()}watchAuthParams(t){if("string"==typeof t)try{this._authParams=JSON.parse(t)}catch{console.error("Failed to parse authParams JSON")}else this._authParams=t}applyThemeStyles(){const t=(e=this._theme,{...f(this._branding),...p(e)});var e;!function(t,e){Object.entries(e).forEach((([e,i])=>{t.style.setProperty(e,i)}))}(this.el,t)}focusFirstInput(){requestAnimationFrame((()=>{const t=this.el.shadowRoot;if(!t)return;const e=t.querySelectorAll("authhero-node");for(const t of Array.from(e)){const e=t.shadowRoot;if(e){const t=e.querySelector('input:not([type="hidden"]):not([type="checkbox"]):not([disabled]), textarea:not([disabled])');if(t)return void t.focus()}}}))}get shouldAutoNavigate(){return this.autoNavigate??this.autoSubmit}buildUrl(t){return this.baseUrl?new URL(t,this.baseUrl).toString():t}loadPersistedState(){if("url"===this.statePersistence){const t=new URL(window.location.href).searchParams.get("state");t&&!this.state&&(this.state=t)}else if("session"===this.statePersistence)try{const t=sessionStorage.getItem(`${this.storageKey}_state`);t&&!this.state&&(this.state=t);const e=sessionStorage.getItem(`${this.storageKey}_screenId`);e&&!this.screenId&&(this.screenId=e)}catch{}}persistState(){if("url"===this.statePersistence){const t=new URL(window.location.href);this.state&&t.searchParams.set("state",this.state),this.screenId&&t.searchParams.set("screen",this.screenId),window.history.replaceState({},"",t.toString())}else if("session"===this.statePersistence)try{this.state&&sessionStorage.setItem(`${this.storageKey}_state`,this.state),this.screenId&&sessionStorage.setItem(`${this.storageKey}_screenId`,this.screenId)}catch{}}handlePopState=t=>{if(!this.apiUrl)return;t.state?.state&&(this.state=t.state.state);const e=t.state?.screen??this.extractScreenIdFromHref(location.href);e&&this.fetchScreen(e)};connectedCallback(){window.addEventListener("popstate",this.handlePopState)}disconnectedCallback(){window.removeEventListener("popstate",this.handlePopState)}async componentWillLoad(){if(!this._screen){const t=this.screen||this.el?.getAttribute("screen");t&&this.watchScreen(t)}this._branding||this.watchBranding(this.branding),this._theme||this.watchTheme(this.theme),this._authParams||this.watchAuthParams(this.authParams),this.loadPersistedState(),this.apiUrl&&!this._screen&&await this.fetchScreen(this.screenId)}async fetchScreen(t,e){if(!this.apiUrl)return!1;const i=t||this.screenId;let s=this.apiUrl;i&&s.includes("{screenId}")&&(s=s.replace("{screenId}",encodeURIComponent(i)));const o=new URL(s,this.baseUrl||window.location.origin);this.state&&o.searchParams.set("state",this.state),e&&o.searchParams.set("nodeId",e),this.loading=!0;try{const t=await fetch(this.buildUrl(o.pathname+o.search),{credentials:"include",headers:{Accept:"application/json"}});if(t.ok){const e=await t.json();if(e.screen?(this._screen=e.screen,e.branding&&(this._branding=e.branding,this.applyThemeStyles()),e.state&&(this.state=e.state),e.screenId&&(this.screenId=e.screenId)):this._screen=e,this._screen)return i&&i!==this.screenId&&(this.screenId=i),this.initFormDataFromDefaults(this._screen),this.screenChange.emit(this._screen),this.updateDataScreenAttribute(),this.persistState(),this.focusFirstInput(),!0}else{const e=await t.json().catch((()=>({message:"Failed to load screen"})));this.flowError.emit({message:e.message||"Failed to load screen"})}}catch(t){console.error("Failed to fetch screen:",t),this.flowError.emit({message:t instanceof Error?t.message:"Failed to fetch screen"})}finally{this.loading=!1}return!1}handleInputChange=(t,e)=>{this.formData={...this.formData,[t]:e}};handleSubmit=async(t,e)=>{if(t.preventDefault(),!this._screen||this.loading)return;const i=e||this.formData;if(this.formSubmit.emit({screen:this._screen,data:i}),this.autoSubmit)if(this._screen.method&&"GET"!==this._screen.method.toUpperCase()){this.loading=!0;try{const t=await fetch(this.buildUrl(this._screen.action),{method:this._screen.method,credentials:"include",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({data:i})}),e=t.headers.get("content-type");if(e?.includes("application/json")){const e=await t.json();e.redirect?(this.flowComplete.emit({redirectUrl:e.redirect}),this.navigate.emit({url:e.redirect}),this.shouldAutoNavigate&&(window.location.href=e.redirect)):!t.ok&&e.screen?(this._screen=e.screen,this.initFormDataFromDefaults(e.screen),this.screenChange.emit(e.screen),this.updateDataScreenAttribute(),this.focusFirstInput()):e.screen?(this._screen=e.screen,this.formData={},this.initFormDataFromDefaults(e.screen),this.screenChange.emit(e.screen),this.updateDataScreenAttribute(),e.screenId&&(this.screenId=e.screenId),this.persistState(),e.navigateUrl&&this.shouldAutoNavigate&&window.history.pushState({screen:e.screenId,state:this.state},"",e.navigateUrl),e.branding&&(this._branding=e.branding,this.applyThemeStyles()),e.state&&(this.state=e.state,this.persistState()),this.focusFirstInput()):e.complete?this.flowComplete.emit({}):!t.ok&&e.error&&(this._screen&&(this._screen={...this._screen,messages:[...this._screen.messages||[],{text:e.error,type:"error"}]}),this.flowError.emit({message:e.error}))}}catch(t){console.error("Form submission failed:",t),this.flowError.emit({message:t instanceof Error?t.message:"Form submission failed"})}finally{this.loading=!1}}else window.location.href=this.buildUrl(this._screen.action)};handleButtonClick=t=>{if("submit"!==t.type)if(this.buttonClick.emit(t),"SOCIAL"===t.type&&t.value&&this.shouldAutoNavigate){const e=this.getProviderHref(t.value);if(e){const t=this.extractScreenIdFromHref(e);return void(t&&this.apiUrl?this.navigateToScreen(t,e):window.location.href=e)}this.handleSocialLogin(t.value)}else"RESEND_BUTTON"===t.type&&this.shouldAutoNavigate&&this.handleResend();else{if((!this._screen?.method||"GET"===this._screen.method.toUpperCase())&&this._screen?.action)return void(window.location.href=this.buildUrl(this._screen.action));const e={...this.formData,[t.id]:"true"};this.formData=e,this.handleSubmit({preventDefault:()=>{}},e)}};handleSocialLogin(t){const e=this._authParams||{},i={connection:t};this.state?i.state=this.state:e.state&&(i.state=e.state),e.client_id&&(i.client_id=e.client_id),e.redirect_uri&&(i.redirect_uri=e.redirect_uri),e.scope&&(i.scope=e.scope),e.audience&&(i.audience=e.audience),e.nonce&&(i.nonce=e.nonce),e.response_type&&(i.response_type=e.response_type);const s=this.buildUrl("/authorize?"+new URLSearchParams(i).toString());this.navigate.emit({url:s}),window.location.href=s}async handleResend(){if(this._screen?.action)try{const t=this._screen.action+(this._screen.action.includes("?")?"&":"?")+"action=resend";await fetch(this.buildUrl(t),{method:"POST",credentials:"include"})}catch(t){console.error("Resend failed:",t)}}extractScreenIdFromHref(t){try{const e=new URL(t,window.location.origin).pathname,i=e.match(/\/u2\/login\/([^/]+)$/);if(i)return i[1];const s=e.match(/\/u2\/([^/]+)$/);return s&&"login"!==s[1]&&"screen"!==s[1]?s[1]:null}catch{return null}}handleLinkClick=(t,e)=>{if(this.linkClick.emit({id:e.id,href:e.href,text:e.text}),!this.shouldAutoNavigate)return void t.preventDefault();const i=this.extractScreenIdFromHref(e.href);return i&&this.apiUrl?(t.preventDefault(),void this.navigateToScreen(i,e.href)):void 0};async navigateToScreen(t,e){await this.fetchScreen(t)?window.history.pushState({screen:t,state:this.state},"",e):window.location.href=e}getProviderHref(t){if(!this._screen)return null;for(const e of this._screen.components){const i=e;if("SOCIAL"===i.type&&i.config?.provider_details){const e=i.config.provider_details.find((e=>e.name===t));if(e?.href)return e.href}}return null}isSocialComponent(t){return"SOCIAL"===t.type}isDividerComponent(t){return"DIVIDER"===t.type}render(){const t=this._screen;if(this.loading&&!t)return s("div",{class:"widget-container"},s("div",{class:"loading-spinner"}));if(!t)return s("div",{class:"widget-container"},s("div",{class:"error-message"},"No screen configuration provided"));const e=t.messages?.filter((t=>"error"===t.type))||[],i=t.messages?.filter((t=>"success"===t.type))||[],o=[...t.components??[]].filter((t=>!1!==t.visible)).sort(((t,e)=>(t.order??0)-(e.order??0))),n=o.filter((t=>this.isSocialComponent(t))),r=o.filter((t=>!this.isSocialComponent(t)&&!this.isDividerComponent(t))),a=o.find((t=>this.isDividerComponent(t))),c=!!a,h=a?.config?.text||"Or",l=t=>{const e=t.config;return["social-buttons","button","button-secondary","button-social","button-social-content","button-social-text","button-social-subtitle","social-icon",...(e?.providers??[]).flatMap((t=>{const e=t.replace(/[^a-zA-Z0-9-]/g,"-");return[`button-social-${e}`,`button-social-content-${e}`,`button-social-text-${e}`,`button-social-subtitle-${e}`,`social-icon-${e}`]}))].join(", ")},d=this._theme?.widget?.logo_url||this._branding?.logo_url;return s("div",{class:"widget-container",part:"container","data-authstack-container":!0},s("header",{class:"widget-header",part:"header"},d&&s("div",{class:"logo-wrapper",part:"logo-wrapper"},s("img",{class:"logo",part:"logo",src:d,alt:"Logo"})),t.title&&s("h1",{class:"title",part:"title",innerHTML:g(t.title)}),t.description&&s("p",{class:"description",part:"description",innerHTML:g(t.description)})),s("div",{class:"widget-body",part:"body"},e.map((t=>s("div",{class:"message message-error",part:"message message-error",key:t.id??t.text},t.text))),i.map((t=>s("div",{class:"message message-success",part:"message message-success",key:t.id??t.text},t.text))),s("form",{onSubmit:this.handleSubmit,part:"form"},s("div",{class:"form-content"},n.length>0&&s("div",{class:"social-section",part:"social-section"},n.map((t=>s("authhero-node",{key:t.id,component:t,value:this.formData[t.id],onFieldChange:t=>this.handleInputChange(t.detail.id,t.detail.value),onButtonClick:t=>this.handleButtonClick(t.detail),disabled:this.loading,exportparts:l(t)})))),n.length>0&&r.length>0&&c&&s("div",{class:"divider",part:"divider"},s("span",{class:"divider-text"},h)),s("div",{class:"fields-section",part:"fields-section"},r.map((t=>s("authhero-node",{key:t.id,component:t,value:this.formData[t.id],onFieldChange:t=>this.handleInputChange(t.detail.id,t.detail.value),onButtonClick:t=>this.handleButtonClick(t.detail),disabled:this.loading})))))),t.links&&t.links.length>0&&s("div",{class:"links",part:"links"},t.links.map((t=>s("span",{class:"link-wrapper",part:"link-wrapper",key:t.id??t.href},t.linkText?s("span",null,t.text," ",s("a",{href:t.href,class:"link",part:"link",onClick:e=>this.handleLinkClick(e,{id:t.id,href:t.href,text:t.linkText||t.text})},t.linkText)):s("a",{href:t.href,class:"link",part:"link",onClick:e=>this.handleLinkClick(e,{id:t.id,href:t.href,text:t.text})},t.text))))),t.footer&&s("div",{class:"widget-footer",part:"footer",innerHTML:g(t.footer)})))}static get watchers(){return{screenId:[{watchScreenId:0}],screen:[{watchScreen:0}],branding:[{watchBranding:0}],theme:[{watchTheme:0}],authParams:[{watchAuthParams:0}]}}};w.style=":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, 16px);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{width:var(--ah-widget-max-width, 400px);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 24px);color:var(--ah-color-header, #1e212a);line-height:1.2}.description{font-size:var(--ah-font-size-subtitle, 16px);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}.widget-footer{margin-top:16px;text-align:center;font-size:12px;color:var(--ah-color-text-muted, #65676e)}.widget-footer a{color:var(--ah-color-link, #635dff);text-decoration:var(--ah-link-decoration, none);font-size:12px;transition:color 150ms ease}.widget-footer a:hover{text-decoration:underline}.widget-footer a: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;width:100%;margin:0}.widget-header{padding:24px 16px 16px}.widget-body{padding:0 16px 24px}}";export{w as authhero_widget}
|