@sparkvault/sdk 1.23.2 → 1.23.3
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.
|
@@ -122,6 +122,12 @@ export declare class IdentityRenderer {
|
|
|
122
122
|
private setupSparkLinkWsHandler;
|
|
123
123
|
/**
|
|
124
124
|
* Handle successful SparkLink verification via WebSocket push.
|
|
125
|
+
*
|
|
126
|
+
* Always invokes the host callback with the full result (including any
|
|
127
|
+
* redirect URL) before attempting top-level navigation. The host page
|
|
128
|
+
* is the source of truth for what should happen after verification:
|
|
129
|
+
* the SDK's window.location.href call is a fallback for hosts that
|
|
130
|
+
* delegate navigation entirely to the SDK.
|
|
125
131
|
*/
|
|
126
132
|
private handleSparkLinkVerified;
|
|
127
133
|
/**
|
package/dist/sparkvault.cjs.js
CHANGED
|
@@ -5450,14 +5450,9 @@ class IdentityRenderer {
|
|
|
5450
5450
|
const currentMethod = this.verificationState.totp.method ?? 'email';
|
|
5451
5451
|
const result = await this.totpHandler.verify(code);
|
|
5452
5452
|
if (result.success && result.result) {
|
|
5453
|
-
//
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
window.location.href = result.result.redirect;
|
|
5457
|
-
return;
|
|
5458
|
-
}
|
|
5459
|
-
// Check if we should prompt for passkey registration
|
|
5460
|
-
if (await this.shouldShowPasskeyPrompt()) {
|
|
5453
|
+
// Passkey upsell only when there's no redirect target — with one,
|
|
5454
|
+
// we head to the destination immediately.
|
|
5455
|
+
if (!result.result.redirect && await this.shouldShowPasskeyPrompt()) {
|
|
5461
5456
|
this.setState({
|
|
5462
5457
|
view: 'passkey-prompt',
|
|
5463
5458
|
email: this.recipient,
|
|
@@ -5466,7 +5461,18 @@ class IdentityRenderer {
|
|
|
5466
5461
|
return;
|
|
5467
5462
|
}
|
|
5468
5463
|
this.close();
|
|
5469
|
-
|
|
5464
|
+
// Hand the full result (including redirect) to the host first so the
|
|
5465
|
+
// page-level handler can act before we navigate away.
|
|
5466
|
+
try {
|
|
5467
|
+
this.callbacks.onSuccess(result.result);
|
|
5468
|
+
}
|
|
5469
|
+
catch (err) {
|
|
5470
|
+
// eslint-disable-next-line no-console
|
|
5471
|
+
console.warn('[SparkVault SDK] onSuccess threw, continuing to redirect', err);
|
|
5472
|
+
}
|
|
5473
|
+
if (typeof result.result.redirect === 'string' && result.result.redirect) {
|
|
5474
|
+
window.location.href = result.result.redirect;
|
|
5475
|
+
}
|
|
5470
5476
|
}
|
|
5471
5477
|
else {
|
|
5472
5478
|
// Check if error is expiry - auto-resend if so
|
|
@@ -5529,14 +5535,17 @@ class IdentityRenderer {
|
|
|
5529
5535
|
? await this.passkeyHandler.register()
|
|
5530
5536
|
: await this.passkeyHandler.verify();
|
|
5531
5537
|
if (result.success && result.result) {
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
this.
|
|
5538
|
+
this.close();
|
|
5539
|
+
try {
|
|
5540
|
+
this.callbacks.onSuccess(result.result);
|
|
5541
|
+
}
|
|
5542
|
+
catch (err) {
|
|
5543
|
+
// eslint-disable-next-line no-console
|
|
5544
|
+
console.warn('[SparkVault SDK] onSuccess threw, continuing to redirect', err);
|
|
5545
|
+
}
|
|
5546
|
+
if (typeof result.result.redirect === 'string' && result.result.redirect) {
|
|
5535
5547
|
window.location.href = result.result.redirect;
|
|
5536
|
-
return;
|
|
5537
5548
|
}
|
|
5538
|
-
this.close();
|
|
5539
|
-
this.callbacks.onSuccess(result.result);
|
|
5540
5549
|
}
|
|
5541
5550
|
else {
|
|
5542
5551
|
// Handle different error types
|
|
@@ -5574,14 +5583,17 @@ class IdentityRenderer {
|
|
|
5574
5583
|
const pendingResult = this.verificationState.passkey.pendingResult;
|
|
5575
5584
|
if (pendingResult) {
|
|
5576
5585
|
this.verificationState.setPendingResult(null);
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
this.
|
|
5586
|
+
this.close();
|
|
5587
|
+
try {
|
|
5588
|
+
this.callbacks.onSuccess(pendingResult);
|
|
5589
|
+
}
|
|
5590
|
+
catch (err) {
|
|
5591
|
+
// eslint-disable-next-line no-console
|
|
5592
|
+
console.warn('[SparkVault SDK] onSuccess threw, continuing to redirect', err);
|
|
5593
|
+
}
|
|
5594
|
+
if (typeof pendingResult.redirect === 'string' && pendingResult.redirect) {
|
|
5580
5595
|
window.location.href = pendingResult.redirect;
|
|
5581
|
-
return;
|
|
5582
5596
|
}
|
|
5583
|
-
this.close();
|
|
5584
|
-
this.callbacks.onSuccess(pendingResult);
|
|
5585
5597
|
}
|
|
5586
5598
|
}
|
|
5587
5599
|
handleSocialLogin(provider) {
|
|
@@ -5663,15 +5675,18 @@ class IdentityRenderer {
|
|
|
5663
5675
|
// Directly trigger passkey registration
|
|
5664
5676
|
const result = await this.passkeyHandler.register();
|
|
5665
5677
|
if (result.success && result.result) {
|
|
5666
|
-
// Handle redirect for OIDC/simple mode flows
|
|
5667
|
-
if (result.result.redirect) {
|
|
5668
|
-
this.close();
|
|
5669
|
-
window.location.href = result.result.redirect;
|
|
5670
|
-
return;
|
|
5671
|
-
}
|
|
5672
5678
|
// Passkey created successfully - use the new token
|
|
5673
5679
|
this.close();
|
|
5674
|
-
|
|
5680
|
+
try {
|
|
5681
|
+
this.callbacks.onSuccess(result.result);
|
|
5682
|
+
}
|
|
5683
|
+
catch (err) {
|
|
5684
|
+
// eslint-disable-next-line no-console
|
|
5685
|
+
console.warn('[SparkVault SDK] onSuccess threw, continuing to redirect', err);
|
|
5686
|
+
}
|
|
5687
|
+
if (typeof result.result.redirect === 'string' && result.result.redirect) {
|
|
5688
|
+
window.location.href = result.result.redirect;
|
|
5689
|
+
}
|
|
5675
5690
|
}
|
|
5676
5691
|
else if (result.errorType === 'cancelled') {
|
|
5677
5692
|
// User cancelled browser dialog - go back to prompt so they can skip or try again
|
|
@@ -5698,15 +5713,18 @@ class IdentityRenderer {
|
|
|
5698
5713
|
handlePasskeyPromptSkip(pendingResult) {
|
|
5699
5714
|
// Set 30-day cookie to suppress future prompts
|
|
5700
5715
|
this.verificationState.dismissPasskeyPrompt();
|
|
5701
|
-
// Handle redirect for OIDC/simple mode flows
|
|
5702
|
-
if (pendingResult.redirect) {
|
|
5703
|
-
this.close();
|
|
5704
|
-
window.location.href = pendingResult.redirect;
|
|
5705
|
-
return;
|
|
5706
|
-
}
|
|
5707
5716
|
// Complete the verification
|
|
5708
5717
|
this.close();
|
|
5709
|
-
|
|
5718
|
+
try {
|
|
5719
|
+
this.callbacks.onSuccess(pendingResult);
|
|
5720
|
+
}
|
|
5721
|
+
catch (err) {
|
|
5722
|
+
// eslint-disable-next-line no-console
|
|
5723
|
+
console.warn('[SparkVault SDK] onSuccess threw, continuing to redirect', err);
|
|
5724
|
+
}
|
|
5725
|
+
if (typeof pendingResult.redirect === 'string' && pendingResult.redirect) {
|
|
5726
|
+
window.location.href = pendingResult.redirect;
|
|
5727
|
+
}
|
|
5710
5728
|
}
|
|
5711
5729
|
/**
|
|
5712
5730
|
* Open a WebSocket connection and return the connectionId.
|
|
@@ -5808,6 +5826,19 @@ class IdentityRenderer {
|
|
|
5808
5826
|
catch {
|
|
5809
5827
|
return;
|
|
5810
5828
|
}
|
|
5829
|
+
// Diagnostic: dump the parsed message so we can see on the client
|
|
5830
|
+
// EXACTLY what arrives. Keys + redirect prefix tell us whether the
|
|
5831
|
+
// field made it across API Gateway. Remove once we've confirmed.
|
|
5832
|
+
// eslint-disable-next-line no-console
|
|
5833
|
+
console.info('[SparkVault SDK] sparklink WS message', {
|
|
5834
|
+
type: data.type,
|
|
5835
|
+
keys: Object.keys(data),
|
|
5836
|
+
hasRedirect: typeof data.redirect === 'string',
|
|
5837
|
+
redirectPrefix: typeof data.redirect === 'string'
|
|
5838
|
+
? data.redirect.slice(0, 80)
|
|
5839
|
+
: null,
|
|
5840
|
+
rawLength: event.data.length,
|
|
5841
|
+
});
|
|
5811
5842
|
switch (data.type) {
|
|
5812
5843
|
case 'sparklink_verified':
|
|
5813
5844
|
this.handleSparkLinkVerified({
|
|
@@ -5846,17 +5877,18 @@ class IdentityRenderer {
|
|
|
5846
5877
|
}
|
|
5847
5878
|
/**
|
|
5848
5879
|
* Handle successful SparkLink verification via WebSocket push.
|
|
5880
|
+
*
|
|
5881
|
+
* Always invokes the host callback with the full result (including any
|
|
5882
|
+
* redirect URL) before attempting top-level navigation. The host page
|
|
5883
|
+
* is the source of truth for what should happen after verification:
|
|
5884
|
+
* the SDK's window.location.href call is a fallback for hosts that
|
|
5885
|
+
* delegate navigation entirely to the SDK.
|
|
5849
5886
|
*/
|
|
5850
5887
|
async handleSparkLinkVerified(result) {
|
|
5851
5888
|
this.cleanupSparkLink();
|
|
5852
|
-
//
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
window.location.href = result.redirect;
|
|
5856
|
-
return;
|
|
5857
|
-
}
|
|
5858
|
-
// Check if we should prompt for passkey registration
|
|
5859
|
-
if (await this.shouldShowPasskeyPrompt()) {
|
|
5889
|
+
// Passkey upsell only when there's no redirect target — once we have
|
|
5890
|
+
// a destination we go there immediately, no upsell.
|
|
5891
|
+
if (!result.redirect && await this.shouldShowPasskeyPrompt()) {
|
|
5860
5892
|
this.setState({
|
|
5861
5893
|
view: 'passkey-prompt',
|
|
5862
5894
|
email: this.recipient,
|
|
@@ -5865,7 +5897,22 @@ class IdentityRenderer {
|
|
|
5865
5897
|
return;
|
|
5866
5898
|
}
|
|
5867
5899
|
this.close();
|
|
5868
|
-
|
|
5900
|
+
// Hand the full result (including redirect) to the host first so the
|
|
5901
|
+
// page-level handler can do app-specific work (logging, navigation,
|
|
5902
|
+
// etc.) before we steal the browsing context with a navigation.
|
|
5903
|
+
try {
|
|
5904
|
+
this.callbacks.onSuccess(result);
|
|
5905
|
+
}
|
|
5906
|
+
catch (err) {
|
|
5907
|
+
// eslint-disable-next-line no-console
|
|
5908
|
+
console.warn('[SparkVault SDK] onSuccess threw, continuing to redirect', err);
|
|
5909
|
+
}
|
|
5910
|
+
// SDK-side fallback navigation. If the host already navigated this is
|
|
5911
|
+
// a no-op (the document is already unloading); if not, this rescues
|
|
5912
|
+
// simple-mode flows whose host forgot to act on result.redirect.
|
|
5913
|
+
if (typeof result.redirect === 'string' && result.redirect) {
|
|
5914
|
+
window.location.href = result.redirect;
|
|
5915
|
+
}
|
|
5869
5916
|
}
|
|
5870
5917
|
/**
|
|
5871
5918
|
* Clean up SparkLink WebSocket connection.
|