@authhero/widget 0.7.2 → 0.8.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.
@@ -1,4 +1,4 @@
1
- import { h } from "@stencil/core";
1
+ import { h, } from "@stencil/core";
2
2
  import { mergeThemeVars, applyCssVars } from "../../utils/branding";
3
3
  export class AuthheroWidget {
4
4
  el;
@@ -11,8 +11,43 @@ export class AuthheroWidget {
11
11
  /**
12
12
  * API endpoint to fetch the initial screen from.
13
13
  * If provided, the widget will fetch the screen on load.
14
+ * Can include {screenId} placeholder which will be replaced with the current screen.
15
+ * Example: "/u2/screen/{screenId}" or "https://auth.example.com/u2/screen/{screenId}"
14
16
  */
15
17
  apiUrl;
18
+ /**
19
+ * Base URL for all API calls. Used when widget is embedded on a different domain.
20
+ * If not provided, relative URLs are used.
21
+ * Example: "https://auth.example.com"
22
+ */
23
+ baseUrl;
24
+ /**
25
+ * Login session state token. Required for social login and maintaining session.
26
+ */
27
+ state;
28
+ /**
29
+ * Current screen ID. Used with apiUrl to fetch screen configuration.
30
+ * When statePersistence is 'url', this is synced with the URL.
31
+ */
32
+ screenId;
33
+ /**
34
+ * OAuth/OIDC parameters for social login redirects.
35
+ * Can be passed as a JSON string or object.
36
+ */
37
+ authParams;
38
+ /**
39
+ * Where to persist state and screen ID.
40
+ * - 'url': Updates URL path/query (default for standalone pages)
41
+ * - 'session': Uses sessionStorage (for embedded widgets)
42
+ * - 'memory': No persistence, state only in memory
43
+ * @default 'memory'
44
+ */
45
+ statePersistence = "memory";
46
+ /**
47
+ * Storage key prefix for session/local storage persistence.
48
+ * @default 'authhero_widget'
49
+ */
50
+ storageKey = "authhero_widget";
16
51
  /**
17
52
  * Branding configuration from AuthHero API.
18
53
  * Controls logo, primary color, and page background.
@@ -37,10 +72,21 @@ export class AuthheroWidget {
37
72
  * @default false
38
73
  */
39
74
  autoSubmit = false;
75
+ /**
76
+ * Whether the widget should handle navigation automatically.
77
+ * When true, social login buttons redirect, links navigate, etc.
78
+ * When false, only events are emitted.
79
+ * @default false (same as autoSubmit when not specified)
80
+ */
81
+ autoNavigate;
40
82
  /**
41
83
  * Internal parsed screen state.
42
84
  */
43
85
  _screen;
86
+ /**
87
+ * Internal parsed auth params state.
88
+ */
89
+ _authParams;
44
90
  /**
45
91
  * Internal parsed branding state.
46
92
  */
@@ -87,12 +133,12 @@ export class AuthheroWidget {
87
133
  */
88
134
  screenChange;
89
135
  watchScreen(newValue) {
90
- if (typeof newValue === 'string') {
136
+ if (typeof newValue === "string") {
91
137
  try {
92
138
  this._screen = JSON.parse(newValue);
93
139
  }
94
140
  catch {
95
- console.error('Failed to parse screen JSON');
141
+ console.error("Failed to parse screen JSON");
96
142
  }
97
143
  }
98
144
  else {
@@ -103,12 +149,12 @@ export class AuthheroWidget {
103
149
  }
104
150
  }
105
151
  watchBranding(newValue) {
106
- if (typeof newValue === 'string') {
152
+ if (typeof newValue === "string") {
107
153
  try {
108
154
  this._branding = JSON.parse(newValue);
109
155
  }
110
156
  catch {
111
- console.error('Failed to parse branding JSON');
157
+ console.error("Failed to parse branding JSON");
112
158
  }
113
159
  }
114
160
  else {
@@ -117,12 +163,12 @@ export class AuthheroWidget {
117
163
  this.applyThemeStyles();
118
164
  }
119
165
  watchTheme(newValue) {
120
- if (typeof newValue === 'string') {
166
+ if (typeof newValue === "string") {
121
167
  try {
122
168
  this._theme = JSON.parse(newValue);
123
169
  }
124
170
  catch {
125
- console.error('Failed to parse theme JSON');
171
+ console.error("Failed to parse theme JSON");
126
172
  }
127
173
  }
128
174
  else {
@@ -130,6 +176,19 @@ export class AuthheroWidget {
130
176
  }
131
177
  this.applyThemeStyles();
132
178
  }
179
+ watchAuthParams(newValue) {
180
+ if (typeof newValue === "string") {
181
+ try {
182
+ this._authParams = JSON.parse(newValue);
183
+ }
184
+ catch {
185
+ console.error("Failed to parse authParams JSON");
186
+ }
187
+ }
188
+ else {
189
+ this._authParams = newValue;
190
+ }
191
+ }
133
192
  /**
134
193
  * Apply branding and theme as CSS custom properties
135
194
  */
@@ -137,36 +196,164 @@ export class AuthheroWidget {
137
196
  const vars = mergeThemeVars(this._branding, this._theme);
138
197
  applyCssVars(this.el, vars);
139
198
  }
199
+ /**
200
+ * Get the effective autoNavigate value (defaults to autoSubmit if not set)
201
+ */
202
+ get shouldAutoNavigate() {
203
+ return this.autoNavigate ?? this.autoSubmit;
204
+ }
205
+ /**
206
+ * Build the full URL for API calls
207
+ */
208
+ buildUrl(path) {
209
+ if (this.baseUrl) {
210
+ return new URL(path, this.baseUrl).toString();
211
+ }
212
+ return path;
213
+ }
214
+ /**
215
+ * Load state from URL or storage based on statePersistence setting
216
+ */
217
+ loadPersistedState() {
218
+ if (this.statePersistence === "url") {
219
+ const url = new URL(window.location.href);
220
+ const stateParam = url.searchParams.get("state");
221
+ if (stateParam && !this.state) {
222
+ this.state = stateParam;
223
+ }
224
+ }
225
+ else if (this.statePersistence === "session") {
226
+ try {
227
+ const stored = sessionStorage.getItem(`${this.storageKey}_state`);
228
+ if (stored && !this.state) {
229
+ this.state = stored;
230
+ }
231
+ const storedScreenId = sessionStorage.getItem(`${this.storageKey}_screenId`);
232
+ if (storedScreenId && !this.screenId) {
233
+ this.screenId = storedScreenId;
234
+ }
235
+ }
236
+ catch {
237
+ // sessionStorage not available
238
+ }
239
+ }
240
+ }
241
+ /**
242
+ * Save state to URL or storage based on statePersistence setting
243
+ */
244
+ persistState() {
245
+ if (this.statePersistence === "url") {
246
+ const url = new URL(window.location.href);
247
+ if (this.state) {
248
+ url.searchParams.set("state", this.state);
249
+ }
250
+ if (this.screenId) {
251
+ url.searchParams.set("screen", this.screenId);
252
+ }
253
+ window.history.replaceState({}, "", url.toString());
254
+ }
255
+ else if (this.statePersistence === "session") {
256
+ try {
257
+ if (this.state) {
258
+ sessionStorage.setItem(`${this.storageKey}_state`, this.state);
259
+ }
260
+ if (this.screenId) {
261
+ sessionStorage.setItem(`${this.storageKey}_screenId`, this.screenId);
262
+ }
263
+ }
264
+ catch {
265
+ // sessionStorage not available
266
+ }
267
+ }
268
+ }
140
269
  async componentWillLoad() {
141
270
  // Parse initial props
142
271
  this.watchScreen(this.screen);
143
272
  this.watchBranding(this.branding);
144
273
  this.watchTheme(this.theme);
274
+ this.watchAuthParams(this.authParams);
275
+ // Load persisted state if available
276
+ this.loadPersistedState();
145
277
  // Fetch screen from API if URL provided and no screen prop
146
278
  if (this.apiUrl && !this._screen) {
147
- await this.fetchScreen();
279
+ await this.fetchScreen(this.screenId);
148
280
  }
149
281
  }
150
- async fetchScreen() {
282
+ /**
283
+ * Fetch screen configuration from the API
284
+ * @param screenIdOverride Optional screen ID to fetch (overrides this.screenId)
285
+ * @param nodeId Optional node ID for flow navigation
286
+ */
287
+ async fetchScreen(screenIdOverride, nodeId) {
151
288
  if (!this.apiUrl)
152
289
  return;
290
+ const currentScreenId = screenIdOverride || this.screenId;
291
+ // Build the API URL, replacing {screenId} placeholder if present
292
+ let url = this.apiUrl;
293
+ if (currentScreenId && url.includes("{screenId}")) {
294
+ url = url.replace("{screenId}", encodeURIComponent(currentScreenId));
295
+ }
296
+ // Add state and nodeId as query params
297
+ const urlObj = new URL(url, this.baseUrl || window.location.origin);
298
+ if (this.state) {
299
+ urlObj.searchParams.set("state", this.state);
300
+ }
301
+ if (nodeId) {
302
+ urlObj.searchParams.set("nodeId", nodeId);
303
+ }
153
304
  this.loading = true;
154
305
  try {
155
- const response = await fetch(this.apiUrl, {
156
- credentials: 'include',
306
+ const response = await fetch(this.buildUrl(urlObj.pathname + urlObj.search), {
307
+ credentials: "include",
157
308
  headers: {
158
- Accept: 'application/json',
309
+ Accept: "application/json",
159
310
  },
160
311
  });
161
312
  if (response.ok) {
162
- this._screen = await response.json();
313
+ const data = await response.json();
314
+ // Handle different response formats
315
+ if (data.screen) {
316
+ this._screen = data.screen;
317
+ if (data.branding) {
318
+ this._branding = data.branding;
319
+ this.applyThemeStyles();
320
+ }
321
+ // Update state if returned
322
+ if (data.state) {
323
+ this.state = data.state;
324
+ }
325
+ // Update screenId if returned in response
326
+ if (data.screenId) {
327
+ this.screenId = data.screenId;
328
+ }
329
+ }
330
+ else {
331
+ // Response is the screen itself
332
+ this._screen = data;
333
+ }
163
334
  if (this._screen) {
335
+ // If we fetched with a screenId override, update our stored screenId
336
+ if (currentScreenId && currentScreenId !== this.screenId) {
337
+ this.screenId = currentScreenId;
338
+ }
164
339
  this.screenChange.emit(this._screen);
340
+ this.persistState();
165
341
  }
166
342
  }
343
+ else {
344
+ const error = await response
345
+ .json()
346
+ .catch(() => ({ message: "Failed to load screen" }));
347
+ this.flowError.emit({
348
+ message: error.message || "Failed to load screen",
349
+ });
350
+ }
167
351
  }
168
352
  catch (error) {
169
- console.error('Failed to fetch screen:', error);
353
+ console.error("Failed to fetch screen:", error);
354
+ this.flowError.emit({
355
+ message: error instanceof Error ? error.message : "Failed to fetch screen",
356
+ });
170
357
  }
171
358
  finally {
172
359
  this.loading = false;
@@ -194,17 +381,17 @@ export class AuthheroWidget {
194
381
  // Submit to the server
195
382
  this.loading = true;
196
383
  try {
197
- const response = await fetch(this._screen.action, {
384
+ const response = await fetch(this.buildUrl(this._screen.action), {
198
385
  method: this._screen.method,
199
- credentials: 'include',
386
+ credentials: "include",
200
387
  headers: {
201
- 'Content-Type': 'application/json',
202
- Accept: 'application/json',
388
+ "Content-Type": "application/json",
389
+ Accept: "application/json",
203
390
  },
204
391
  body: JSON.stringify({ data: this.formData }),
205
392
  });
206
- const contentType = response.headers.get('content-type');
207
- if (contentType?.includes('application/json')) {
393
+ const contentType = response.headers.get("content-type");
394
+ if (contentType?.includes("application/json")) {
208
395
  const result = await response.json();
209
396
  // Handle different response types
210
397
  if (result.redirect) {
@@ -212,17 +399,31 @@ export class AuthheroWidget {
212
399
  this.flowComplete.emit({ redirectUrl: result.redirect });
213
400
  // Also emit navigate for backwards compatibility
214
401
  this.navigate.emit({ url: result.redirect });
402
+ // Auto-navigate if enabled
403
+ if (this.shouldAutoNavigate) {
404
+ window.location.href = result.redirect;
405
+ }
215
406
  }
216
407
  else if (result.screen) {
217
408
  // Next screen
218
409
  this._screen = result.screen;
219
410
  this.formData = {};
220
411
  this.screenChange.emit(result.screen);
412
+ // Update screenId if returned in response
413
+ if (result.screenId) {
414
+ this.screenId = result.screenId;
415
+ }
416
+ this.persistState();
221
417
  // Apply branding if included
222
418
  if (result.branding) {
223
419
  this._branding = result.branding;
224
420
  this.applyThemeStyles();
225
421
  }
422
+ // Update state if returned
423
+ if (result.state) {
424
+ this.state = result.state;
425
+ this.persistState();
426
+ }
226
427
  }
227
428
  else if (result.complete) {
228
429
  // Flow complete without redirect
@@ -236,9 +437,9 @@ export class AuthheroWidget {
236
437
  }
237
438
  }
238
439
  catch (err) {
239
- console.error('Form submission failed:', err);
440
+ console.error("Form submission failed:", err);
240
441
  this.flowError.emit({
241
- message: err instanceof Error ? err.message : 'Form submission failed',
442
+ message: err instanceof Error ? err.message : "Form submission failed",
242
443
  });
243
444
  }
244
445
  finally {
@@ -247,14 +448,79 @@ export class AuthheroWidget {
247
448
  };
248
449
  handleButtonClick = (detail) => {
249
450
  // If this is a submit button click, trigger form submission
250
- if (detail.type === 'submit') {
451
+ if (detail.type === "submit") {
251
452
  // Create a synthetic submit event and call handleSubmit
252
453
  const syntheticEvent = { preventDefault: () => { } };
253
454
  this.handleSubmit(syntheticEvent);
254
455
  return;
255
456
  }
457
+ // Always emit the event
256
458
  this.buttonClick.emit(detail);
459
+ // Handle social login if autoNavigate is enabled
460
+ if (detail.type === "SOCIAL" && detail.value && this.shouldAutoNavigate) {
461
+ this.handleSocialLogin(detail.value);
462
+ return;
463
+ }
464
+ // Handle resend button
465
+ if (detail.type === "RESEND_BUTTON" && this.shouldAutoNavigate) {
466
+ this.handleResend();
467
+ return;
468
+ }
257
469
  };
470
+ /**
471
+ * Handle social login redirect
472
+ */
473
+ handleSocialLogin(connection) {
474
+ const params = this._authParams || {};
475
+ const queryParams = {
476
+ connection,
477
+ };
478
+ // Add state
479
+ if (this.state) {
480
+ queryParams.state = this.state;
481
+ }
482
+ else if (params.state) {
483
+ queryParams.state = params.state;
484
+ }
485
+ // Add client_id
486
+ if (params.client_id) {
487
+ queryParams.client_id = params.client_id;
488
+ }
489
+ // Add optional params
490
+ if (params.redirect_uri)
491
+ queryParams.redirect_uri = params.redirect_uri;
492
+ if (params.scope)
493
+ queryParams.scope = params.scope;
494
+ if (params.audience)
495
+ queryParams.audience = params.audience;
496
+ if (params.nonce)
497
+ queryParams.nonce = params.nonce;
498
+ if (params.response_type)
499
+ queryParams.response_type = params.response_type;
500
+ const socialUrl = this.buildUrl("/authorize?" + new URLSearchParams(queryParams).toString());
501
+ // Emit navigate event and redirect
502
+ this.navigate.emit({ url: socialUrl });
503
+ window.location.href = socialUrl;
504
+ }
505
+ /**
506
+ * Handle resend button click (e.g., resend OTP code)
507
+ */
508
+ async handleResend() {
509
+ if (!this._screen?.action)
510
+ return;
511
+ try {
512
+ const url = this._screen.action +
513
+ (this._screen.action.includes("?") ? "&" : "?") +
514
+ "action=resend";
515
+ await fetch(this.buildUrl(url), {
516
+ method: "POST",
517
+ credentials: "include",
518
+ });
519
+ }
520
+ catch (error) {
521
+ console.error("Resend failed:", error);
522
+ }
523
+ }
258
524
  handleLinkClick = (e, link) => {
259
525
  // Emit the event so the consuming app can handle it
260
526
  this.linkClick.emit({
@@ -262,9 +528,9 @@ export class AuthheroWidget {
262
528
  href: link.href,
263
529
  text: link.text,
264
530
  });
265
- // If autoSubmit is enabled, let the browser handle the navigation
531
+ // If autoNavigate is enabled, let the browser handle the navigation
266
532
  // Otherwise, prevent default and let the app decide
267
- if (!this.autoSubmit) {
533
+ if (!this.shouldAutoNavigate) {
268
534
  e.preventDefault();
269
535
  }
270
536
  };
@@ -272,13 +538,13 @@ export class AuthheroWidget {
272
538
  * Get error messages from the screen-level messages array.
273
539
  */
274
540
  getScreenErrors() {
275
- return this._screen?.messages?.filter((m) => m.type === 'error') || [];
541
+ return this._screen?.messages?.filter((m) => m.type === "error") || [];
276
542
  }
277
543
  /**
278
544
  * Get success messages from the screen-level messages array.
279
545
  */
280
546
  getScreenSuccesses() {
281
- return this._screen?.messages?.filter((m) => m.type === 'success') || [];
547
+ return this._screen?.messages?.filter((m) => m.type === "success") || [];
282
548
  }
283
549
  /**
284
550
  * Sort components by order.
@@ -296,13 +562,13 @@ export class AuthheroWidget {
296
562
  isSocialComponent(component) {
297
563
  // Check the type property directly - FormComponent has a 'type' field
298
564
  // SocialField has type 'SOCIAL'
299
- return component.type === 'SOCIAL';
565
+ return component.type === "SOCIAL";
300
566
  }
301
567
  /**
302
568
  * Check if a component is a divider.
303
569
  */
304
570
  isDividerComponent(component) {
305
- return component.type === 'DIVIDER';
571
+ return component.type === "DIVIDER";
306
572
  }
307
573
  render() {
308
574
  if (this.loading && !this._screen) {
@@ -315,12 +581,22 @@ export class AuthheroWidget {
315
581
  const screenSuccesses = this.getScreenSuccesses();
316
582
  const components = this.getOrderedComponents();
317
583
  // Separate social, divider, and field components for layout ordering
318
- const socialComponents = components.filter(c => this.isSocialComponent(c));
319
- const fieldComponents = components.filter(c => !this.isSocialComponent(c) && !this.isDividerComponent(c));
320
- const hasDivider = components.some(c => this.isDividerComponent(c));
584
+ const socialComponents = components.filter((c) => this.isSocialComponent(c));
585
+ const fieldComponents = components.filter((c) => !this.isSocialComponent(c) && !this.isDividerComponent(c));
586
+ const hasDivider = components.some((c) => this.isDividerComponent(c));
321
587
  // Get logo URL from theme.widget (takes precedence) or branding
322
588
  const logoUrl = this._theme?.widget?.logo_url || this._branding?.logo_url;
323
- 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" }, this._screen.title)), this._screen.description && (h("p", { class: "description", part: "description" }, 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 && fieldComponents.length > 0 && 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, { id: link.id, href: link.href, text: link.linkText || link.text }) }, link.linkText))) : (h("a", { href: link.href, class: "link", part: "link", onClick: (e) => this.handleLinkClick(e, { id: link.id, href: link.href, text: link.text }) }, link.text))))))))));
589
+ 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" }, this._screen.title)), this._screen.description && (h("p", { class: "description", part: "description" }, 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 &&
590
+ fieldComponents.length > 0 &&
591
+ 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, {
592
+ id: link.id,
593
+ href: link.href,
594
+ text: link.linkText || link.text,
595
+ }) }, link.linkText))) : (h("a", { href: link.href, class: "link", part: "link", onClick: (e) => this.handleLinkClick(e, {
596
+ id: link.id,
597
+ href: link.href,
598
+ text: link.text,
599
+ }) }, link.text))))))))));
324
600
  }
325
601
  static get is() { return "authhero-widget"; }
326
602
  static get encapsulation() { return "shadow"; }
@@ -374,13 +650,147 @@ export class AuthheroWidget {
374
650
  "optional": true,
375
651
  "docs": {
376
652
  "tags": [],
377
- "text": "API endpoint to fetch the initial screen from.\nIf provided, the widget will fetch the screen on load."
653
+ "text": "API endpoint to fetch the initial screen from.\nIf provided, the widget will fetch the screen on load.\nCan include {screenId} placeholder which will be replaced with the current screen.\nExample: \"/u2/screen/{screenId}\" or \"https://auth.example.com/u2/screen/{screenId}\""
378
654
  },
379
655
  "getter": false,
380
656
  "setter": false,
381
657
  "reflect": false,
382
658
  "attribute": "api-url"
383
659
  },
660
+ "baseUrl": {
661
+ "type": "string",
662
+ "mutable": false,
663
+ "complexType": {
664
+ "original": "string",
665
+ "resolved": "string | undefined",
666
+ "references": {}
667
+ },
668
+ "required": false,
669
+ "optional": true,
670
+ "docs": {
671
+ "tags": [],
672
+ "text": "Base URL for all API calls. Used when widget is embedded on a different domain.\nIf not provided, relative URLs are used.\nExample: \"https://auth.example.com\""
673
+ },
674
+ "getter": false,
675
+ "setter": false,
676
+ "reflect": false,
677
+ "attribute": "base-url"
678
+ },
679
+ "state": {
680
+ "type": "string",
681
+ "mutable": true,
682
+ "complexType": {
683
+ "original": "string",
684
+ "resolved": "string | undefined",
685
+ "references": {}
686
+ },
687
+ "required": false,
688
+ "optional": true,
689
+ "docs": {
690
+ "tags": [],
691
+ "text": "Login session state token. Required for social login and maintaining session."
692
+ },
693
+ "getter": false,
694
+ "setter": false,
695
+ "reflect": false,
696
+ "attribute": "state"
697
+ },
698
+ "screenId": {
699
+ "type": "string",
700
+ "mutable": true,
701
+ "complexType": {
702
+ "original": "string",
703
+ "resolved": "string | undefined",
704
+ "references": {}
705
+ },
706
+ "required": false,
707
+ "optional": true,
708
+ "docs": {
709
+ "tags": [],
710
+ "text": "Current screen ID. Used with apiUrl to fetch screen configuration.\nWhen statePersistence is 'url', this is synced with the URL."
711
+ },
712
+ "getter": false,
713
+ "setter": false,
714
+ "reflect": false,
715
+ "attribute": "screen-id"
716
+ },
717
+ "authParams": {
718
+ "type": "string",
719
+ "mutable": false,
720
+ "complexType": {
721
+ "original": "AuthParams | string",
722
+ "resolved": "AuthParams | string | undefined",
723
+ "references": {
724
+ "AuthParams": {
725
+ "location": "local",
726
+ "path": "/home/runner/work/authhero/authhero/packages/ui-widget/src/components/authhero-widget/authhero-widget.tsx",
727
+ "id": "src/components/authhero-widget/authhero-widget.tsx::AuthParams"
728
+ }
729
+ }
730
+ },
731
+ "required": false,
732
+ "optional": true,
733
+ "docs": {
734
+ "tags": [],
735
+ "text": "OAuth/OIDC parameters for social login redirects.\nCan be passed as a JSON string or object."
736
+ },
737
+ "getter": false,
738
+ "setter": false,
739
+ "reflect": false,
740
+ "attribute": "auth-params"
741
+ },
742
+ "statePersistence": {
743
+ "type": "string",
744
+ "mutable": false,
745
+ "complexType": {
746
+ "original": "StatePersistence",
747
+ "resolved": "\"memory\" | \"session\" | \"url\"",
748
+ "references": {
749
+ "StatePersistence": {
750
+ "location": "local",
751
+ "path": "/home/runner/work/authhero/authhero/packages/ui-widget/src/components/authhero-widget/authhero-widget.tsx",
752
+ "id": "src/components/authhero-widget/authhero-widget.tsx::StatePersistence"
753
+ }
754
+ }
755
+ },
756
+ "required": false,
757
+ "optional": false,
758
+ "docs": {
759
+ "tags": [{
760
+ "name": "default",
761
+ "text": "'memory'"
762
+ }],
763
+ "text": "Where to persist state and screen ID.\n- 'url': Updates URL path/query (default for standalone pages)\n- 'session': Uses sessionStorage (for embedded widgets)\n- 'memory': No persistence, state only in memory"
764
+ },
765
+ "getter": false,
766
+ "setter": false,
767
+ "reflect": false,
768
+ "attribute": "state-persistence",
769
+ "defaultValue": "\"memory\""
770
+ },
771
+ "storageKey": {
772
+ "type": "string",
773
+ "mutable": false,
774
+ "complexType": {
775
+ "original": "string",
776
+ "resolved": "string",
777
+ "references": {}
778
+ },
779
+ "required": false,
780
+ "optional": false,
781
+ "docs": {
782
+ "tags": [{
783
+ "name": "default",
784
+ "text": "'authhero_widget'"
785
+ }],
786
+ "text": "Storage key prefix for session/local storage persistence."
787
+ },
788
+ "getter": false,
789
+ "setter": false,
790
+ "reflect": false,
791
+ "attribute": "storage-key",
792
+ "defaultValue": "\"authhero_widget\""
793
+ },
384
794
  "branding": {
385
795
  "type": "string",
386
796
  "mutable": false,
@@ -475,12 +885,35 @@ export class AuthheroWidget {
475
885
  "reflect": false,
476
886
  "attribute": "auto-submit",
477
887
  "defaultValue": "false"
888
+ },
889
+ "autoNavigate": {
890
+ "type": "boolean",
891
+ "mutable": false,
892
+ "complexType": {
893
+ "original": "boolean",
894
+ "resolved": "boolean | undefined",
895
+ "references": {}
896
+ },
897
+ "required": false,
898
+ "optional": true,
899
+ "docs": {
900
+ "tags": [{
901
+ "name": "default",
902
+ "text": "false (same as autoSubmit when not specified)"
903
+ }],
904
+ "text": "Whether the widget should handle navigation automatically.\nWhen true, social login buttons redirect, links navigate, etc.\nWhen false, only events are emitted."
905
+ },
906
+ "getter": false,
907
+ "setter": false,
908
+ "reflect": false,
909
+ "attribute": "auto-navigate"
478
910
  }
479
911
  };
480
912
  }
481
913
  static get states() {
482
914
  return {
483
915
  "_screen": {},
916
+ "_authParams": {},
484
917
  "_branding": {},
485
918
  "_theme": {},
486
919
  "formData": {}
@@ -648,6 +1081,9 @@ export class AuthheroWidget {
648
1081
  }, {
649
1082
  "propName": "theme",
650
1083
  "methodName": "watchTheme"
1084
+ }, {
1085
+ "propName": "authParams",
1086
+ "methodName": "watchAuthParams"
651
1087
  }];
652
1088
  }
653
1089
  }