@edge-markets/connect-link 1.3.0 → 1.5.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/README.md +10 -3
- package/dist/index.d.mts +83 -6
- package/dist/index.d.ts +83 -6
- package/dist/index.js +66 -4
- package/dist/index.mjs +71 -21
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -94,11 +94,19 @@ interface EdgeLinkConfig {
|
|
|
94
94
|
onEvent?: (event) => void // Called for analytics events
|
|
95
95
|
scopes?: EdgeScope[] // Scopes to request (default: all)
|
|
96
96
|
linkUrl?: string // Custom Link URL (dev only)
|
|
97
|
-
redirectUri?: string //
|
|
97
|
+
redirectUri?: string // Legacy popup callback URI (migration only)
|
|
98
98
|
}
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
Popup integrations are origin-based. Register your frontend origin with EDGE
|
|
102
|
+
for `postMessage` validation, for example:
|
|
103
|
+
|
|
104
|
+
- `https://app.partner.com`
|
|
105
|
+
- `http://localhost:3000`
|
|
106
|
+
- `http://localhost:5173`
|
|
107
|
+
|
|
108
|
+
`redirectUri` is no longer required for popup completion. Keep it only while
|
|
109
|
+
migrating an older integration that still sends a registered callback URL.
|
|
102
110
|
|
|
103
111
|
## Callbacks
|
|
104
112
|
|
|
@@ -323,4 +331,3 @@ MIT
|
|
|
323
331
|
|
|
324
332
|
|
|
325
333
|
|
|
326
|
-
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { EdgeLinkConfigBase, EdgeScope, EdgeLinkSuccess, EdgeLinkExit, PKCEPair } from '@edge-markets/connect';
|
|
2
|
-
export { ALL_EDGE_SCOPES, EDGE_SCOPES, EdgeEnvironment, EdgeError, EdgeLinkEvent, EdgeLinkEventName, EdgeLinkExit, EdgeLinkSuccess, EdgePopupBlockedError, EdgeScope, EdgeStateMismatchError, PKCEPair, isEdgeError } from '@edge-markets/connect';
|
|
1
|
+
import { EdgeLinkConfigBase, EdgeScope, EdgeLinkSuccess, EdgeLinkExit, SdkGeolocation, PKCEPair } from '@edge-markets/connect';
|
|
2
|
+
export { ALL_EDGE_SCOPES, EDGE_SCOPES, EdgeEnvironment, EdgeError, EdgeLinkEvent, EdgeLinkEventName, EdgeLinkExit, EdgeLinkSuccess, EdgePopupBlockedError, EdgeScope, EdgeStateMismatchError, PKCEPair, SdkGeolocation, isEdgeError } from '@edge-markets/connect';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* EdgeLink - Popup Authentication for EDGE Connect
|
|
@@ -53,10 +53,11 @@ export { ALL_EDGE_SCOPES, EDGE_SCOPES, EdgeEnvironment, EdgeError, EdgeLinkEvent
|
|
|
53
53
|
*/
|
|
54
54
|
interface EdgeLinkConfig extends EdgeLinkConfigBase {
|
|
55
55
|
/**
|
|
56
|
-
*
|
|
57
|
-
* Must be registered in your OAuth client settings.
|
|
56
|
+
* Legacy redirect URI to include in the popup request.
|
|
58
57
|
*
|
|
59
|
-
*
|
|
58
|
+
* Popup completion uses `postMessage` to your frontend origin and does not
|
|
59
|
+
* require a callback path by default. Keep this only while migrating older
|
|
60
|
+
* integrations that still expect a registered callback URL.
|
|
60
61
|
*/
|
|
61
62
|
redirectUri?: string;
|
|
62
63
|
}
|
|
@@ -99,6 +100,7 @@ declare class EdgeLink {
|
|
|
99
100
|
private state;
|
|
100
101
|
private messageHandler;
|
|
101
102
|
private isDestroyed;
|
|
103
|
+
private isInitializing;
|
|
102
104
|
/**
|
|
103
105
|
* Creates a new EdgeLink instance.
|
|
104
106
|
*
|
|
@@ -253,6 +255,7 @@ declare function useEdgeLink(config: UseEdgeLinkConfig): UseEdgeLinkReturn;
|
|
|
253
255
|
* verify.destroy()
|
|
254
256
|
* ```
|
|
255
257
|
*/
|
|
258
|
+
|
|
256
259
|
/**
|
|
257
260
|
* Event types emitted during the transfer verification flow.
|
|
258
261
|
*
|
|
@@ -418,6 +421,25 @@ interface EdgeTransferVerifyConfig {
|
|
|
418
421
|
* ```
|
|
419
422
|
*/
|
|
420
423
|
onEvent?: (event: TransferVerifyEvent) => void;
|
|
424
|
+
/**
|
|
425
|
+
* SDK-reported geolocation to forward to the verification page.
|
|
426
|
+
*
|
|
427
|
+
* Collect this using `collectGeolocation()` before opening the verification UI.
|
|
428
|
+
* The server cross-references it against IP-based geolocation for stronger
|
|
429
|
+
* state-level enforcement.
|
|
430
|
+
*
|
|
431
|
+
* @example
|
|
432
|
+
* ```typescript
|
|
433
|
+
* import { collectGeolocation } from '@edge-markets/connect-link'
|
|
434
|
+
*
|
|
435
|
+
* const geo = await collectGeolocation()
|
|
436
|
+
* const verify = new EdgeTransferVerify({
|
|
437
|
+
* // ...
|
|
438
|
+
* geolocation: geo ?? undefined,
|
|
439
|
+
* })
|
|
440
|
+
* ```
|
|
441
|
+
*/
|
|
442
|
+
geolocation?: SdkGeolocation;
|
|
421
443
|
}
|
|
422
444
|
/**
|
|
423
445
|
* EdgeTransferVerify - Transfer verification launcher for EDGE Connect.
|
|
@@ -600,6 +622,8 @@ declare class EdgeTransferVerify {
|
|
|
600
622
|
* Handles the loaded event from the verification page.
|
|
601
623
|
*
|
|
602
624
|
* The verification UI is ready for user interaction.
|
|
625
|
+
* If geolocation data was provided, forwards it to the verification page
|
|
626
|
+
* via postMessage for server-side cross-referencing.
|
|
603
627
|
*/
|
|
604
628
|
private handleLoaded;
|
|
605
629
|
/**
|
|
@@ -718,6 +742,59 @@ declare class IframeManager {
|
|
|
718
742
|
private hideLoadingState;
|
|
719
743
|
}
|
|
720
744
|
|
|
745
|
+
/**
|
|
746
|
+
* Browser Geolocation Collector for EDGE Connect
|
|
747
|
+
*
|
|
748
|
+
* Collects the user's geolocation from the Browser Geolocation API.
|
|
749
|
+
* The result can be passed to {@link EdgeTransferVerify} for cross-referencing
|
|
750
|
+
* against server-side IP-based geolocation.
|
|
751
|
+
*
|
|
752
|
+
* This function **never throws** — it returns `null` on permission denial,
|
|
753
|
+
* timeout, or API unavailability.
|
|
754
|
+
*
|
|
755
|
+
* @module @edge-markets/connect-link
|
|
756
|
+
*/
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Options for geolocation collection.
|
|
760
|
+
*/
|
|
761
|
+
interface GeolocationOptions {
|
|
762
|
+
/** Timeout in milliseconds (default: 10000) */
|
|
763
|
+
timeout?: number;
|
|
764
|
+
/** Maximum age of a cached position in milliseconds (default: 60000) */
|
|
765
|
+
maximumAge?: number;
|
|
766
|
+
/** Whether to request high-accuracy positioning (default: false) */
|
|
767
|
+
enableHighAccuracy?: boolean;
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* Collects the user's geolocation from the Browser Geolocation API.
|
|
771
|
+
*
|
|
772
|
+
* Returns `null` silently if:
|
|
773
|
+
* - The Geolocation API is not available
|
|
774
|
+
* - The user denies permission
|
|
775
|
+
* - The request times out
|
|
776
|
+
* - Any other error occurs
|
|
777
|
+
*
|
|
778
|
+
* @param options - Optional configuration for the geolocation request
|
|
779
|
+
* @returns The user's geolocation, or `null` if unavailable
|
|
780
|
+
*
|
|
781
|
+
* @example
|
|
782
|
+
* ```typescript
|
|
783
|
+
* import { collectGeolocation, EdgeTransferVerify } from '@edge-markets/connect-link'
|
|
784
|
+
*
|
|
785
|
+
* const geo = await collectGeolocation()
|
|
786
|
+
*
|
|
787
|
+
* const verify = new EdgeTransferVerify({
|
|
788
|
+
* verificationUrl: session.verificationUrl,
|
|
789
|
+
* sessionId: session.sessionId,
|
|
790
|
+
* container: document.getElementById('verify-container')!,
|
|
791
|
+
* geolocation: geo ?? undefined,
|
|
792
|
+
* onSuccess: (event) => console.log('Verified!'),
|
|
793
|
+
* })
|
|
794
|
+
* ```
|
|
795
|
+
*/
|
|
796
|
+
declare function collectGeolocation(options?: GeolocationOptions): Promise<SdkGeolocation | null>;
|
|
797
|
+
|
|
721
798
|
/**
|
|
722
799
|
* PKCE (Proof Key for Code Exchange) Utilities
|
|
723
800
|
*
|
|
@@ -802,4 +879,4 @@ declare function generateState(): string;
|
|
|
802
879
|
*/
|
|
803
880
|
declare function assertCryptoAvailable(): void;
|
|
804
881
|
|
|
805
|
-
export { EdgeLink, type EdgeLinkConfig, type EdgeLinkOpenOptions, EdgeTransferVerify, type EdgeTransferVerifyConfig, type IframeCallbacks, type IframeConfig, IframeManager, type TransferVerifyEvent, type TransferVerifyEventType, type TransferVerifyMode, type UseEdgeLinkConfig, type UseEdgeLinkReturn, assertCryptoAvailable, generatePKCE, generateState, useEdgeLink };
|
|
882
|
+
export { EdgeLink, type EdgeLinkConfig, type EdgeLinkOpenOptions, EdgeTransferVerify, type EdgeTransferVerifyConfig, type GeolocationOptions, type IframeCallbacks, type IframeConfig, IframeManager, type TransferVerifyEvent, type TransferVerifyEventType, type TransferVerifyMode, type UseEdgeLinkConfig, type UseEdgeLinkReturn, assertCryptoAvailable, collectGeolocation, generatePKCE, generateState, useEdgeLink };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { EdgeLinkConfigBase, EdgeScope, EdgeLinkSuccess, EdgeLinkExit, PKCEPair } from '@edge-markets/connect';
|
|
2
|
-
export { ALL_EDGE_SCOPES, EDGE_SCOPES, EdgeEnvironment, EdgeError, EdgeLinkEvent, EdgeLinkEventName, EdgeLinkExit, EdgeLinkSuccess, EdgePopupBlockedError, EdgeScope, EdgeStateMismatchError, PKCEPair, isEdgeError } from '@edge-markets/connect';
|
|
1
|
+
import { EdgeLinkConfigBase, EdgeScope, EdgeLinkSuccess, EdgeLinkExit, SdkGeolocation, PKCEPair } from '@edge-markets/connect';
|
|
2
|
+
export { ALL_EDGE_SCOPES, EDGE_SCOPES, EdgeEnvironment, EdgeError, EdgeLinkEvent, EdgeLinkEventName, EdgeLinkExit, EdgeLinkSuccess, EdgePopupBlockedError, EdgeScope, EdgeStateMismatchError, PKCEPair, SdkGeolocation, isEdgeError } from '@edge-markets/connect';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* EdgeLink - Popup Authentication for EDGE Connect
|
|
@@ -53,10 +53,11 @@ export { ALL_EDGE_SCOPES, EDGE_SCOPES, EdgeEnvironment, EdgeError, EdgeLinkEvent
|
|
|
53
53
|
*/
|
|
54
54
|
interface EdgeLinkConfig extends EdgeLinkConfigBase {
|
|
55
55
|
/**
|
|
56
|
-
*
|
|
57
|
-
* Must be registered in your OAuth client settings.
|
|
56
|
+
* Legacy redirect URI to include in the popup request.
|
|
58
57
|
*
|
|
59
|
-
*
|
|
58
|
+
* Popup completion uses `postMessage` to your frontend origin and does not
|
|
59
|
+
* require a callback path by default. Keep this only while migrating older
|
|
60
|
+
* integrations that still expect a registered callback URL.
|
|
60
61
|
*/
|
|
61
62
|
redirectUri?: string;
|
|
62
63
|
}
|
|
@@ -99,6 +100,7 @@ declare class EdgeLink {
|
|
|
99
100
|
private state;
|
|
100
101
|
private messageHandler;
|
|
101
102
|
private isDestroyed;
|
|
103
|
+
private isInitializing;
|
|
102
104
|
/**
|
|
103
105
|
* Creates a new EdgeLink instance.
|
|
104
106
|
*
|
|
@@ -253,6 +255,7 @@ declare function useEdgeLink(config: UseEdgeLinkConfig): UseEdgeLinkReturn;
|
|
|
253
255
|
* verify.destroy()
|
|
254
256
|
* ```
|
|
255
257
|
*/
|
|
258
|
+
|
|
256
259
|
/**
|
|
257
260
|
* Event types emitted during the transfer verification flow.
|
|
258
261
|
*
|
|
@@ -418,6 +421,25 @@ interface EdgeTransferVerifyConfig {
|
|
|
418
421
|
* ```
|
|
419
422
|
*/
|
|
420
423
|
onEvent?: (event: TransferVerifyEvent) => void;
|
|
424
|
+
/**
|
|
425
|
+
* SDK-reported geolocation to forward to the verification page.
|
|
426
|
+
*
|
|
427
|
+
* Collect this using `collectGeolocation()` before opening the verification UI.
|
|
428
|
+
* The server cross-references it against IP-based geolocation for stronger
|
|
429
|
+
* state-level enforcement.
|
|
430
|
+
*
|
|
431
|
+
* @example
|
|
432
|
+
* ```typescript
|
|
433
|
+
* import { collectGeolocation } from '@edge-markets/connect-link'
|
|
434
|
+
*
|
|
435
|
+
* const geo = await collectGeolocation()
|
|
436
|
+
* const verify = new EdgeTransferVerify({
|
|
437
|
+
* // ...
|
|
438
|
+
* geolocation: geo ?? undefined,
|
|
439
|
+
* })
|
|
440
|
+
* ```
|
|
441
|
+
*/
|
|
442
|
+
geolocation?: SdkGeolocation;
|
|
421
443
|
}
|
|
422
444
|
/**
|
|
423
445
|
* EdgeTransferVerify - Transfer verification launcher for EDGE Connect.
|
|
@@ -600,6 +622,8 @@ declare class EdgeTransferVerify {
|
|
|
600
622
|
* Handles the loaded event from the verification page.
|
|
601
623
|
*
|
|
602
624
|
* The verification UI is ready for user interaction.
|
|
625
|
+
* If geolocation data was provided, forwards it to the verification page
|
|
626
|
+
* via postMessage for server-side cross-referencing.
|
|
603
627
|
*/
|
|
604
628
|
private handleLoaded;
|
|
605
629
|
/**
|
|
@@ -718,6 +742,59 @@ declare class IframeManager {
|
|
|
718
742
|
private hideLoadingState;
|
|
719
743
|
}
|
|
720
744
|
|
|
745
|
+
/**
|
|
746
|
+
* Browser Geolocation Collector for EDGE Connect
|
|
747
|
+
*
|
|
748
|
+
* Collects the user's geolocation from the Browser Geolocation API.
|
|
749
|
+
* The result can be passed to {@link EdgeTransferVerify} for cross-referencing
|
|
750
|
+
* against server-side IP-based geolocation.
|
|
751
|
+
*
|
|
752
|
+
* This function **never throws** — it returns `null` on permission denial,
|
|
753
|
+
* timeout, or API unavailability.
|
|
754
|
+
*
|
|
755
|
+
* @module @edge-markets/connect-link
|
|
756
|
+
*/
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Options for geolocation collection.
|
|
760
|
+
*/
|
|
761
|
+
interface GeolocationOptions {
|
|
762
|
+
/** Timeout in milliseconds (default: 10000) */
|
|
763
|
+
timeout?: number;
|
|
764
|
+
/** Maximum age of a cached position in milliseconds (default: 60000) */
|
|
765
|
+
maximumAge?: number;
|
|
766
|
+
/** Whether to request high-accuracy positioning (default: false) */
|
|
767
|
+
enableHighAccuracy?: boolean;
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* Collects the user's geolocation from the Browser Geolocation API.
|
|
771
|
+
*
|
|
772
|
+
* Returns `null` silently if:
|
|
773
|
+
* - The Geolocation API is not available
|
|
774
|
+
* - The user denies permission
|
|
775
|
+
* - The request times out
|
|
776
|
+
* - Any other error occurs
|
|
777
|
+
*
|
|
778
|
+
* @param options - Optional configuration for the geolocation request
|
|
779
|
+
* @returns The user's geolocation, or `null` if unavailable
|
|
780
|
+
*
|
|
781
|
+
* @example
|
|
782
|
+
* ```typescript
|
|
783
|
+
* import { collectGeolocation, EdgeTransferVerify } from '@edge-markets/connect-link'
|
|
784
|
+
*
|
|
785
|
+
* const geo = await collectGeolocation()
|
|
786
|
+
*
|
|
787
|
+
* const verify = new EdgeTransferVerify({
|
|
788
|
+
* verificationUrl: session.verificationUrl,
|
|
789
|
+
* sessionId: session.sessionId,
|
|
790
|
+
* container: document.getElementById('verify-container')!,
|
|
791
|
+
* geolocation: geo ?? undefined,
|
|
792
|
+
* onSuccess: (event) => console.log('Verified!'),
|
|
793
|
+
* })
|
|
794
|
+
* ```
|
|
795
|
+
*/
|
|
796
|
+
declare function collectGeolocation(options?: GeolocationOptions): Promise<SdkGeolocation | null>;
|
|
797
|
+
|
|
721
798
|
/**
|
|
722
799
|
* PKCE (Proof Key for Code Exchange) Utilities
|
|
723
800
|
*
|
|
@@ -802,4 +879,4 @@ declare function generateState(): string;
|
|
|
802
879
|
*/
|
|
803
880
|
declare function assertCryptoAvailable(): void;
|
|
804
881
|
|
|
805
|
-
export { EdgeLink, type EdgeLinkConfig, type EdgeLinkOpenOptions, EdgeTransferVerify, type EdgeTransferVerifyConfig, type IframeCallbacks, type IframeConfig, IframeManager, type TransferVerifyEvent, type TransferVerifyEventType, type TransferVerifyMode, type UseEdgeLinkConfig, type UseEdgeLinkReturn, assertCryptoAvailable, generatePKCE, generateState, useEdgeLink };
|
|
882
|
+
export { EdgeLink, type EdgeLinkConfig, type EdgeLinkOpenOptions, EdgeTransferVerify, type EdgeTransferVerifyConfig, type GeolocationOptions, type IframeCallbacks, type IframeConfig, IframeManager, type TransferVerifyEvent, type TransferVerifyEventType, type TransferVerifyMode, type UseEdgeLinkConfig, type UseEdgeLinkReturn, assertCryptoAvailable, collectGeolocation, generatePKCE, generateState, useEdgeLink };
|
package/dist/index.js
CHANGED
|
@@ -29,6 +29,7 @@ __export(index_exports, {
|
|
|
29
29
|
EdgeTransferVerify: () => EdgeTransferVerify,
|
|
30
30
|
IframeManager: () => IframeManager,
|
|
31
31
|
assertCryptoAvailable: () => assertCryptoAvailable,
|
|
32
|
+
collectGeolocation: () => collectGeolocation,
|
|
32
33
|
generatePKCE: () => generatePKCE,
|
|
33
34
|
generateState: () => generateState,
|
|
34
35
|
isEdgeError: () => import_connect3.isEdgeError,
|
|
@@ -320,6 +321,16 @@ var PopupManager = class {
|
|
|
320
321
|
};
|
|
321
322
|
|
|
322
323
|
// src/edge-link.ts
|
|
324
|
+
var hasWarnedLegacyRedirectUri = false;
|
|
325
|
+
function warnLegacyRedirectUriUsage(redirectUri) {
|
|
326
|
+
if (hasWarnedLegacyRedirectUri) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
hasWarnedLegacyRedirectUri = true;
|
|
330
|
+
console.warn(
|
|
331
|
+
`EdgeLink: popup flows no longer require redirectUri. Received legacy redirectUri "${redirectUri}". Register your frontend origin with EDGE and remove this option when possible.`
|
|
332
|
+
);
|
|
333
|
+
}
|
|
323
334
|
var EdgeLink = class {
|
|
324
335
|
/**
|
|
325
336
|
* Creates a new EdgeLink instance.
|
|
@@ -332,6 +343,7 @@ var EdgeLink = class {
|
|
|
332
343
|
this.state = null;
|
|
333
344
|
this.messageHandler = null;
|
|
334
345
|
this.isDestroyed = false;
|
|
346
|
+
this.isInitializing = false;
|
|
335
347
|
if (!config.clientId) {
|
|
336
348
|
throw new Error("EdgeLink: clientId is required");
|
|
337
349
|
}
|
|
@@ -376,7 +388,7 @@ var EdgeLink = class {
|
|
|
376
388
|
if (this.isDestroyed) {
|
|
377
389
|
throw new Error("EdgeLink: Cannot open - instance has been destroyed");
|
|
378
390
|
}
|
|
379
|
-
if (this.popup.isOpen()) {
|
|
391
|
+
if (this.popup.isOpen() || this.isInitializing) {
|
|
380
392
|
this.popup.focus();
|
|
381
393
|
return;
|
|
382
394
|
}
|
|
@@ -439,6 +451,7 @@ var EdgeLink = class {
|
|
|
439
451
|
* Initializes PKCE and navigates popup to auth URL.
|
|
440
452
|
*/
|
|
441
453
|
async initializeAuth(scopes) {
|
|
454
|
+
this.isInitializing = true;
|
|
442
455
|
try {
|
|
443
456
|
this.pkce = await generatePKCE();
|
|
444
457
|
this.state = generateState();
|
|
@@ -455,6 +468,8 @@ var EdgeLink = class {
|
|
|
455
468
|
message: error instanceof Error ? error.message : "Failed to initialize"
|
|
456
469
|
}
|
|
457
470
|
});
|
|
471
|
+
} finally {
|
|
472
|
+
this.isInitializing = false;
|
|
458
473
|
}
|
|
459
474
|
}
|
|
460
475
|
/**
|
|
@@ -476,9 +491,12 @@ var EdgeLink = class {
|
|
|
476
491
|
url.searchParams.set("code_challenge_method", "S256");
|
|
477
492
|
const formattedScopes = (0, import_connect.formatScopesForEnvironment)(scopes, this.config.environment);
|
|
478
493
|
url.searchParams.set("scope", formattedScopes.join(" "));
|
|
479
|
-
|
|
480
|
-
url.searchParams.set("redirect_uri", redirectUri);
|
|
494
|
+
url.searchParams.set("flow", "popup");
|
|
481
495
|
url.searchParams.set("origin", window.location.origin);
|
|
496
|
+
if (this.config.redirectUri) {
|
|
497
|
+
url.searchParams.set("redirect_uri", this.config.redirectUri);
|
|
498
|
+
warnLegacyRedirectUriUsage(this.config.redirectUri);
|
|
499
|
+
}
|
|
482
500
|
return url.toString();
|
|
483
501
|
}
|
|
484
502
|
/**
|
|
@@ -486,6 +504,7 @@ var EdgeLink = class {
|
|
|
486
504
|
*/
|
|
487
505
|
setupMessageListener() {
|
|
488
506
|
this.messageHandler = (event) => {
|
|
507
|
+
if (this.isDestroyed) return;
|
|
489
508
|
if (event.origin !== this.expectedOrigin) {
|
|
490
509
|
return;
|
|
491
510
|
}
|
|
@@ -530,7 +549,7 @@ var EdgeLink = class {
|
|
|
530
549
|
}
|
|
531
550
|
});
|
|
532
551
|
this.close();
|
|
533
|
-
|
|
552
|
+
return;
|
|
534
553
|
}
|
|
535
554
|
if (!this.pkce?.verifier) {
|
|
536
555
|
this.config.onExit?.({
|
|
@@ -1160,9 +1179,27 @@ var EdgeTransferVerify = class {
|
|
|
1160
1179
|
* Handles the loaded event from the verification page.
|
|
1161
1180
|
*
|
|
1162
1181
|
* The verification UI is ready for user interaction.
|
|
1182
|
+
* If geolocation data was provided, forwards it to the verification page
|
|
1183
|
+
* via postMessage for server-side cross-referencing.
|
|
1163
1184
|
*/
|
|
1164
1185
|
handleLoaded(event) {
|
|
1165
1186
|
this.config.onLoaded?.(event);
|
|
1187
|
+
if (this.config.geolocation) {
|
|
1188
|
+
const target = this.mode === "popup" ? this.popupManager?.getWindow?.() : this.iframeManager?.getContentWindow?.();
|
|
1189
|
+
if (target) {
|
|
1190
|
+
target.postMessage(
|
|
1191
|
+
{
|
|
1192
|
+
type: "edge:transfer-verify:geo",
|
|
1193
|
+
nonce: this.nonce,
|
|
1194
|
+
latitude: this.config.geolocation.latitude,
|
|
1195
|
+
longitude: this.config.geolocation.longitude,
|
|
1196
|
+
accuracy: this.config.geolocation.accuracy,
|
|
1197
|
+
timestamp: this.config.geolocation.timestamp
|
|
1198
|
+
},
|
|
1199
|
+
this.expectedOrigin
|
|
1200
|
+
);
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1166
1203
|
}
|
|
1167
1204
|
/**
|
|
1168
1205
|
* Handles user closing the popup manually (popup mode only).
|
|
@@ -1184,6 +1221,30 @@ var EdgeTransferVerify = class {
|
|
|
1184
1221
|
}
|
|
1185
1222
|
};
|
|
1186
1223
|
|
|
1224
|
+
// src/geolocation.ts
|
|
1225
|
+
async function collectGeolocation(options) {
|
|
1226
|
+
if (typeof navigator === "undefined" || !navigator.geolocation) {
|
|
1227
|
+
return null;
|
|
1228
|
+
}
|
|
1229
|
+
try {
|
|
1230
|
+
const position = await new Promise((resolve, reject) => {
|
|
1231
|
+
navigator.geolocation.getCurrentPosition(resolve, reject, {
|
|
1232
|
+
timeout: options?.timeout ?? 1e4,
|
|
1233
|
+
maximumAge: options?.maximumAge ?? 6e4,
|
|
1234
|
+
enableHighAccuracy: options?.enableHighAccuracy ?? false
|
|
1235
|
+
});
|
|
1236
|
+
});
|
|
1237
|
+
return {
|
|
1238
|
+
latitude: position.coords.latitude,
|
|
1239
|
+
longitude: position.coords.longitude,
|
|
1240
|
+
accuracy: position.coords.accuracy,
|
|
1241
|
+
timestamp: new Date(position.timestamp).toISOString()
|
|
1242
|
+
};
|
|
1243
|
+
} catch {
|
|
1244
|
+
return null;
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1187
1248
|
// src/index.ts
|
|
1188
1249
|
var import_connect3 = require("@edge-markets/connect");
|
|
1189
1250
|
var import_connect4 = require("@edge-markets/connect");
|
|
@@ -1198,6 +1259,7 @@ var import_connect4 = require("@edge-markets/connect");
|
|
|
1198
1259
|
EdgeTransferVerify,
|
|
1199
1260
|
IframeManager,
|
|
1200
1261
|
assertCryptoAvailable,
|
|
1262
|
+
collectGeolocation,
|
|
1201
1263
|
generatePKCE,
|
|
1202
1264
|
generateState,
|
|
1203
1265
|
isEdgeError,
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
// src/edge-link.ts
|
|
2
2
|
import {
|
|
3
|
-
getEnvironmentConfig,
|
|
4
3
|
ALL_EDGE_SCOPES,
|
|
5
|
-
formatScopesForEnvironment,
|
|
6
4
|
EdgePopupBlockedError,
|
|
7
|
-
|
|
5
|
+
formatScopesForEnvironment,
|
|
6
|
+
getEnvironmentConfig
|
|
8
7
|
} from "@edge-markets/connect";
|
|
9
8
|
|
|
10
9
|
// src/pkce.ts
|
|
@@ -288,6 +287,16 @@ var PopupManager = class {
|
|
|
288
287
|
};
|
|
289
288
|
|
|
290
289
|
// src/edge-link.ts
|
|
290
|
+
var hasWarnedLegacyRedirectUri = false;
|
|
291
|
+
function warnLegacyRedirectUriUsage(redirectUri) {
|
|
292
|
+
if (hasWarnedLegacyRedirectUri) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
hasWarnedLegacyRedirectUri = true;
|
|
296
|
+
console.warn(
|
|
297
|
+
`EdgeLink: popup flows no longer require redirectUri. Received legacy redirectUri "${redirectUri}". Register your frontend origin with EDGE and remove this option when possible.`
|
|
298
|
+
);
|
|
299
|
+
}
|
|
291
300
|
var EdgeLink = class {
|
|
292
301
|
/**
|
|
293
302
|
* Creates a new EdgeLink instance.
|
|
@@ -300,6 +309,7 @@ var EdgeLink = class {
|
|
|
300
309
|
this.state = null;
|
|
301
310
|
this.messageHandler = null;
|
|
302
311
|
this.isDestroyed = false;
|
|
312
|
+
this.isInitializing = false;
|
|
303
313
|
if (!config.clientId) {
|
|
304
314
|
throw new Error("EdgeLink: clientId is required");
|
|
305
315
|
}
|
|
@@ -344,7 +354,7 @@ var EdgeLink = class {
|
|
|
344
354
|
if (this.isDestroyed) {
|
|
345
355
|
throw new Error("EdgeLink: Cannot open - instance has been destroyed");
|
|
346
356
|
}
|
|
347
|
-
if (this.popup.isOpen()) {
|
|
357
|
+
if (this.popup.isOpen() || this.isInitializing) {
|
|
348
358
|
this.popup.focus();
|
|
349
359
|
return;
|
|
350
360
|
}
|
|
@@ -407,6 +417,7 @@ var EdgeLink = class {
|
|
|
407
417
|
* Initializes PKCE and navigates popup to auth URL.
|
|
408
418
|
*/
|
|
409
419
|
async initializeAuth(scopes) {
|
|
420
|
+
this.isInitializing = true;
|
|
410
421
|
try {
|
|
411
422
|
this.pkce = await generatePKCE();
|
|
412
423
|
this.state = generateState();
|
|
@@ -423,6 +434,8 @@ var EdgeLink = class {
|
|
|
423
434
|
message: error instanceof Error ? error.message : "Failed to initialize"
|
|
424
435
|
}
|
|
425
436
|
});
|
|
437
|
+
} finally {
|
|
438
|
+
this.isInitializing = false;
|
|
426
439
|
}
|
|
427
440
|
}
|
|
428
441
|
/**
|
|
@@ -444,9 +457,12 @@ var EdgeLink = class {
|
|
|
444
457
|
url.searchParams.set("code_challenge_method", "S256");
|
|
445
458
|
const formattedScopes = formatScopesForEnvironment(scopes, this.config.environment);
|
|
446
459
|
url.searchParams.set("scope", formattedScopes.join(" "));
|
|
447
|
-
|
|
448
|
-
url.searchParams.set("redirect_uri", redirectUri);
|
|
460
|
+
url.searchParams.set("flow", "popup");
|
|
449
461
|
url.searchParams.set("origin", window.location.origin);
|
|
462
|
+
if (this.config.redirectUri) {
|
|
463
|
+
url.searchParams.set("redirect_uri", this.config.redirectUri);
|
|
464
|
+
warnLegacyRedirectUriUsage(this.config.redirectUri);
|
|
465
|
+
}
|
|
450
466
|
return url.toString();
|
|
451
467
|
}
|
|
452
468
|
/**
|
|
@@ -454,6 +470,7 @@ var EdgeLink = class {
|
|
|
454
470
|
*/
|
|
455
471
|
setupMessageListener() {
|
|
456
472
|
this.messageHandler = (event) => {
|
|
473
|
+
if (this.isDestroyed) return;
|
|
457
474
|
if (event.origin !== this.expectedOrigin) {
|
|
458
475
|
return;
|
|
459
476
|
}
|
|
@@ -498,7 +515,7 @@ var EdgeLink = class {
|
|
|
498
515
|
}
|
|
499
516
|
});
|
|
500
517
|
this.close();
|
|
501
|
-
|
|
518
|
+
return;
|
|
502
519
|
}
|
|
503
520
|
if (!this.pkce?.verifier) {
|
|
504
521
|
this.config.onExit?.({
|
|
@@ -616,9 +633,7 @@ function useEdgeLink(config) {
|
|
|
616
633
|
}
|
|
617
634
|
|
|
618
635
|
// src/edge-transfer-verify.ts
|
|
619
|
-
import {
|
|
620
|
-
EdgePopupBlockedError as EdgePopupBlockedError2
|
|
621
|
-
} from "@edge-markets/connect";
|
|
636
|
+
import { EdgePopupBlockedError as EdgePopupBlockedError2 } from "@edge-markets/connect";
|
|
622
637
|
|
|
623
638
|
// src/iframe-manager.ts
|
|
624
639
|
var DEFAULT_TITLE = "EDGE Connect Transfer Verification";
|
|
@@ -1130,9 +1145,27 @@ var EdgeTransferVerify = class {
|
|
|
1130
1145
|
* Handles the loaded event from the verification page.
|
|
1131
1146
|
*
|
|
1132
1147
|
* The verification UI is ready for user interaction.
|
|
1148
|
+
* If geolocation data was provided, forwards it to the verification page
|
|
1149
|
+
* via postMessage for server-side cross-referencing.
|
|
1133
1150
|
*/
|
|
1134
1151
|
handleLoaded(event) {
|
|
1135
1152
|
this.config.onLoaded?.(event);
|
|
1153
|
+
if (this.config.geolocation) {
|
|
1154
|
+
const target = this.mode === "popup" ? this.popupManager?.getWindow?.() : this.iframeManager?.getContentWindow?.();
|
|
1155
|
+
if (target) {
|
|
1156
|
+
target.postMessage(
|
|
1157
|
+
{
|
|
1158
|
+
type: "edge:transfer-verify:geo",
|
|
1159
|
+
nonce: this.nonce,
|
|
1160
|
+
latitude: this.config.geolocation.latitude,
|
|
1161
|
+
longitude: this.config.geolocation.longitude,
|
|
1162
|
+
accuracy: this.config.geolocation.accuracy,
|
|
1163
|
+
timestamp: this.config.geolocation.timestamp
|
|
1164
|
+
},
|
|
1165
|
+
this.expectedOrigin
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1136
1169
|
}
|
|
1137
1170
|
/**
|
|
1138
1171
|
* Handles user closing the popup manually (popup mode only).
|
|
@@ -1154,27 +1187,44 @@ var EdgeTransferVerify = class {
|
|
|
1154
1187
|
}
|
|
1155
1188
|
};
|
|
1156
1189
|
|
|
1190
|
+
// src/geolocation.ts
|
|
1191
|
+
async function collectGeolocation(options) {
|
|
1192
|
+
if (typeof navigator === "undefined" || !navigator.geolocation) {
|
|
1193
|
+
return null;
|
|
1194
|
+
}
|
|
1195
|
+
try {
|
|
1196
|
+
const position = await new Promise((resolve, reject) => {
|
|
1197
|
+
navigator.geolocation.getCurrentPosition(resolve, reject, {
|
|
1198
|
+
timeout: options?.timeout ?? 1e4,
|
|
1199
|
+
maximumAge: options?.maximumAge ?? 6e4,
|
|
1200
|
+
enableHighAccuracy: options?.enableHighAccuracy ?? false
|
|
1201
|
+
});
|
|
1202
|
+
});
|
|
1203
|
+
return {
|
|
1204
|
+
latitude: position.coords.latitude,
|
|
1205
|
+
longitude: position.coords.longitude,
|
|
1206
|
+
accuracy: position.coords.accuracy,
|
|
1207
|
+
timestamp: new Date(position.timestamp).toISOString()
|
|
1208
|
+
};
|
|
1209
|
+
} catch {
|
|
1210
|
+
return null;
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1157
1214
|
// src/index.ts
|
|
1158
|
-
import {
|
|
1159
|
-
|
|
1160
|
-
EdgeStateMismatchError as EdgeStateMismatchError2,
|
|
1161
|
-
EdgeError,
|
|
1162
|
-
isEdgeError
|
|
1163
|
-
} from "@edge-markets/connect";
|
|
1164
|
-
import {
|
|
1165
|
-
EDGE_SCOPES,
|
|
1166
|
-
ALL_EDGE_SCOPES as ALL_EDGE_SCOPES2
|
|
1167
|
-
} from "@edge-markets/connect";
|
|
1215
|
+
import { EdgeError, EdgePopupBlockedError as EdgePopupBlockedError3, EdgeStateMismatchError, isEdgeError } from "@edge-markets/connect";
|
|
1216
|
+
import { ALL_EDGE_SCOPES as ALL_EDGE_SCOPES2, EDGE_SCOPES } from "@edge-markets/connect";
|
|
1168
1217
|
export {
|
|
1169
1218
|
ALL_EDGE_SCOPES2 as ALL_EDGE_SCOPES,
|
|
1170
1219
|
EDGE_SCOPES,
|
|
1171
1220
|
EdgeError,
|
|
1172
1221
|
EdgeLink,
|
|
1173
1222
|
EdgePopupBlockedError3 as EdgePopupBlockedError,
|
|
1174
|
-
|
|
1223
|
+
EdgeStateMismatchError,
|
|
1175
1224
|
EdgeTransferVerify,
|
|
1176
1225
|
IframeManager,
|
|
1177
1226
|
assertCryptoAvailable,
|
|
1227
|
+
collectGeolocation,
|
|
1178
1228
|
generatePKCE,
|
|
1179
1229
|
generateState,
|
|
1180
1230
|
isEdgeError,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edge-markets/connect-link",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Browser SDK for EDGE Connect popup authentication",
|
|
5
5
|
"author": "EdgeBoost",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@edge-markets/connect": "1.
|
|
24
|
+
"@edge-markets/connect": "^1.5.1"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"react": ">=17.0.0"
|
|
@@ -32,7 +32,9 @@
|
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
+
"@types/jsdom": "^28.0.1",
|
|
35
36
|
"@types/react": "^18.2.0",
|
|
37
|
+
"jsdom": "^29.0.2",
|
|
36
38
|
"tsup": "^8.0.0",
|
|
37
39
|
"typescript": "^5.3.0",
|
|
38
40
|
"vitest": "^1.0.0"
|