@capgo/capacitor-social-login 8.2.7 → 8.2.8

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.
@@ -535,77 +535,124 @@ class GoogleSocialLogin extends BaseSocialLogin {
535
535
  const popup = window.open(url, 'Google Sign In', `width=${width},height=${height},left=${left},top=${top},popup=1`);
536
536
  let popupClosedInterval;
537
537
  let timeoutHandle;
538
+ // Use BroadcastChannel for cross-origin communication (works when postMessage doesn't)
539
+ const channelName = `google_oauth_${nonce || Date.now()}`;
540
+ let broadcastChannel = null;
541
+ try {
542
+ broadcastChannel = new BroadcastChannel(channelName);
543
+ }
544
+ catch (_b) {
545
+ // BroadcastChannel not supported, fall back to postMessage only
546
+ }
538
547
  // This may never return...
539
548
  return new Promise((resolve, reject) => {
540
549
  if (!popup) {
541
550
  reject(new Error('Failed to open popup'));
542
551
  return;
543
552
  }
544
- const handleMessage = (event) => {
545
- var _a, _b, _c, _d;
546
- if (event.origin !== window.location.origin || ((_b = (_a = event.data) === null || _a === void 0 ? void 0 : _a.source) === null || _b === void 0 ? void 0 : _b.startsWith('angular')))
547
- return;
548
- if (((_c = event.data) === null || _c === void 0 ? void 0 : _c.type) === 'oauth-response') {
549
- window.removeEventListener('message', handleMessage);
550
- clearInterval(popupClosedInterval);
551
- clearTimeout(timeoutHandle);
552
- if (this.loginType === 'online') {
553
- const { accessToken, idToken } = event.data;
554
- if (accessToken && idToken) {
555
- const profile = this.parseJwt(idToken);
556
- this.persistStateGoogle(accessToken.token, idToken);
557
- resolve({
558
- provider: 'google',
559
- result: {
560
- accessToken: {
561
- token: accessToken.token,
562
- },
563
- idToken,
564
- profile: {
565
- email: profile.email || null,
566
- familyName: profile.family_name || null,
567
- givenName: profile.given_name || null,
568
- id: profile.sub || null,
569
- name: profile.name || null,
570
- imageUrl: profile.picture || null,
571
- },
572
- responseType: 'online',
573
- },
574
- });
575
- }
576
- }
577
- else {
578
- const { serverAuthCode } = event.data;
553
+ const cleanup = () => {
554
+ window.removeEventListener('message', handleMessage);
555
+ clearInterval(popupClosedInterval);
556
+ clearTimeout(timeoutHandle);
557
+ if (broadcastChannel) {
558
+ broadcastChannel.close();
559
+ }
560
+ };
561
+ const processOAuthResponse = (data) => {
562
+ if (this.loginType === 'online') {
563
+ const { accessToken, idToken } = data;
564
+ if (accessToken && idToken) {
565
+ const profile = this.parseJwt(idToken);
566
+ this.persistStateGoogle(accessToken.token, idToken);
579
567
  resolve({
580
568
  provider: 'google',
581
569
  result: {
582
- responseType: 'offline',
583
- serverAuthCode,
570
+ accessToken: {
571
+ token: accessToken.token,
572
+ },
573
+ idToken,
574
+ profile: {
575
+ email: profile.email || null,
576
+ familyName: profile.family_name || null,
577
+ givenName: profile.given_name || null,
578
+ id: profile.sub || null,
579
+ name: profile.name || null,
580
+ imageUrl: profile.picture || null,
581
+ },
582
+ responseType: 'online',
584
583
  },
585
584
  });
586
585
  }
587
586
  }
587
+ else {
588
+ const { serverAuthCode } = data;
589
+ resolve({
590
+ provider: 'google',
591
+ result: {
592
+ responseType: 'offline',
593
+ serverAuthCode,
594
+ },
595
+ });
596
+ }
597
+ };
598
+ const handleMessage = (event) => {
599
+ var _a, _b, _c, _d;
600
+ if (event.origin !== window.location.origin || ((_b = (_a = event.data) === null || _a === void 0 ? void 0 : _a.source) === null || _b === void 0 ? void 0 : _b.startsWith('angular')))
601
+ return;
602
+ if (((_c = event.data) === null || _c === void 0 ? void 0 : _c.type) === 'oauth-response') {
603
+ cleanup();
604
+ processOAuthResponse(event.data);
605
+ }
588
606
  else if (((_d = event.data) === null || _d === void 0 ? void 0 : _d.type) === 'oauth-error') {
589
- window.removeEventListener('message', handleMessage);
590
- clearInterval(popupClosedInterval);
591
- clearTimeout(timeoutHandle);
607
+ cleanup();
592
608
  const errorMessage = event.data.error || 'User cancelled the OAuth flow';
593
609
  reject(new Error(errorMessage));
594
610
  }
595
611
  // Don't reject for non-OAuth messages, just ignore them
596
612
  };
613
+ // Listen for BroadcastChannel messages
614
+ if (broadcastChannel) {
615
+ broadcastChannel.onmessage = (event) => {
616
+ var _a;
617
+ const data = event.data;
618
+ if ((_a = data === null || data === void 0 ? void 0 : data.source) === null || _a === void 0 ? void 0 : _a.toString().startsWith('angular'))
619
+ return;
620
+ if ((data === null || data === void 0 ? void 0 : data.type) === 'oauth-response') {
621
+ cleanup();
622
+ processOAuthResponse(data);
623
+ }
624
+ else if ((data === null || data === void 0 ? void 0 : data.type) === 'oauth-error') {
625
+ cleanup();
626
+ const errorMessage = data.error || 'User cancelled the OAuth flow';
627
+ reject(new Error(errorMessage));
628
+ }
629
+ };
630
+ }
597
631
  window.addEventListener('message', handleMessage);
598
632
  // Timeout after 5 minutes
599
633
  timeoutHandle = setTimeout(() => {
600
- clearTimeout(timeoutHandle);
601
- window.removeEventListener('message', handleMessage);
602
- popup.close();
634
+ cleanup();
635
+ try {
636
+ popup.close();
637
+ }
638
+ catch (_a) {
639
+ // Ignore cross-origin errors when closing
640
+ }
603
641
  reject(new Error('OAuth timeout'));
604
642
  }, 300000);
605
643
  popupClosedInterval = setInterval(() => {
606
- if (popup.closed) {
644
+ try {
645
+ // Check if popup is closed - this may throw cross-origin errors for some providers
646
+ if (popup.closed) {
647
+ cleanup();
648
+ reject(new Error('Popup closed'));
649
+ }
650
+ }
651
+ catch (_a) {
652
+ // Cross-origin error when checking popup.closed - this happens when the popup
653
+ // navigates to a third-party OAuth provider with strict security settings.
654
+ // We can't detect if the window was closed, so we rely on timeout and message handlers.
607
655
  clearInterval(popupClosedInterval);
608
- reject(new Error('Popup closed'));
609
656
  }
610
657
  }, 1000);
611
658
  });
@@ -725,52 +772,95 @@ class OAuth2SocialLogin extends BaseSocialLogin {
725
772
  reject(new Error('Unable to open login window. Please allow popups.'));
726
773
  return;
727
774
  }
775
+ // Use BroadcastChannel for cross-origin communication (works when postMessage doesn't)
776
+ const channelName = `oauth2_${state}`;
777
+ let broadcastChannel = null;
778
+ try {
779
+ broadcastChannel = new BroadcastChannel(channelName);
780
+ }
781
+ catch (_a) {
782
+ // BroadcastChannel not supported, fall back to postMessage only
783
+ if (config.logsEnabled) {
784
+ console.log(`[OAuth2:${providerId}] BroadcastChannel not supported, using postMessage only`);
785
+ }
786
+ }
728
787
  const cleanup = (messageHandler, timeoutHandle, intervalHandle) => {
729
788
  window.removeEventListener('message', messageHandler);
730
789
  clearTimeout(timeoutHandle);
731
790
  clearInterval(intervalHandle);
732
- };
733
- const messageHandler = (event) => {
734
- var _a, _b, _c, _d, _e;
735
- if (event.origin !== window.location.origin) {
736
- return;
791
+ if (broadcastChannel) {
792
+ broadcastChannel.close();
737
793
  }
738
- if (((_a = event.data) === null || _a === void 0 ? void 0 : _a.type) === 'oauth-response') {
739
- if (((_b = event.data) === null || _b === void 0 ? void 0 : _b.provider) && event.data.provider !== 'oauth2') {
740
- return;
794
+ };
795
+ const handleOAuthMessage = (data) => {
796
+ if ((data === null || data === void 0 ? void 0 : data.type) === 'oauth-response') {
797
+ if ((data === null || data === void 0 ? void 0 : data.provider) && data.provider !== 'oauth2') {
798
+ return false;
741
799
  }
742
800
  // Check providerId matches if present
743
- if (((_c = event.data) === null || _c === void 0 ? void 0 : _c.providerId) && event.data.providerId !== providerId) {
744
- return;
801
+ if ((data === null || data === void 0 ? void 0 : data.providerId) && data.providerId !== providerId) {
802
+ return false;
745
803
  }
746
804
  cleanup(messageHandler, timeoutHandle, popupClosedInterval);
747
805
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
748
- const _f = event.data, { provider: _ignoredProvider, type: _ignoredType } = _f, payload = __rest$1(_f, ["provider", "type"]);
806
+ const _a = data, { provider: _ignoredProvider, type: _ignoredType } = _a, payload = __rest$1(_a, ["provider", "type"]);
749
807
  resolve({
750
808
  provider: 'oauth2',
751
809
  result: payload,
752
810
  });
811
+ return true;
753
812
  }
754
- else if (((_d = event.data) === null || _d === void 0 ? void 0 : _d.type) === 'oauth-error') {
755
- if (((_e = event.data) === null || _e === void 0 ? void 0 : _e.provider) && event.data.provider !== 'oauth2') {
756
- return;
813
+ else if ((data === null || data === void 0 ? void 0 : data.type) === 'oauth-error') {
814
+ if ((data === null || data === void 0 ? void 0 : data.provider) && data.provider !== 'oauth2') {
815
+ return false;
757
816
  }
758
817
  cleanup(messageHandler, timeoutHandle, popupClosedInterval);
759
- reject(new Error(event.data.error || 'OAuth2 login was cancelled.'));
818
+ reject(new Error(data.error || 'OAuth2 login was cancelled.'));
819
+ return true;
760
820
  }
821
+ return false;
822
+ };
823
+ // Listen for BroadcastChannel messages
824
+ if (broadcastChannel) {
825
+ broadcastChannel.onmessage = (event) => {
826
+ handleOAuthMessage(event.data);
827
+ };
828
+ }
829
+ const messageHandler = (event) => {
830
+ if (event.origin !== window.location.origin) {
831
+ return;
832
+ }
833
+ handleOAuthMessage(event.data);
761
834
  };
762
835
  window.addEventListener('message', messageHandler);
763
836
  const timeoutHandle = window.setTimeout(() => {
764
- window.removeEventListener('message', messageHandler);
765
- popup.close();
837
+ cleanup(messageHandler, timeoutHandle, popupClosedInterval);
838
+ try {
839
+ popup.close();
840
+ }
841
+ catch (_a) {
842
+ // Ignore cross-origin errors when closing
843
+ }
766
844
  reject(new Error('OAuth2 login timed out.'));
767
845
  }, 300000);
768
846
  const popupClosedInterval = window.setInterval(() => {
769
- if (popup.closed) {
770
- window.removeEventListener('message', messageHandler);
847
+ try {
848
+ // Check if popup is closed - this may throw cross-origin errors for some providers
849
+ if (popup.closed) {
850
+ cleanup(messageHandler, timeoutHandle, popupClosedInterval);
851
+ reject(new Error('OAuth2 login window was closed.'));
852
+ }
853
+ }
854
+ catch (_a) {
855
+ // Cross-origin error when checking popup.closed - this happens when the popup
856
+ // navigates to a third-party OAuth provider with strict security settings.
857
+ // We can't detect if the window was closed, so we just rely on the timeout
858
+ // and message handlers. Clear the interval to avoid repeated errors.
771
859
  clearInterval(popupClosedInterval);
772
- clearTimeout(timeoutHandle);
773
- reject(new Error('OAuth2 login window was closed.'));
860
+ if (config.logsEnabled) {
861
+ console.log(`[OAuth2:${providerId}] Cannot check popup.closed due to cross-origin restrictions. ` +
862
+ 'Relying on message handlers and timeout.');
863
+ }
774
864
  }
775
865
  }, 1000);
776
866
  });
@@ -1127,48 +1217,83 @@ class TwitterSocialLogin extends BaseSocialLogin {
1127
1217
  reject(new Error('Unable to open login window. Please allow popups.'));
1128
1218
  return;
1129
1219
  }
1220
+ // Use BroadcastChannel for cross-origin communication (works when postMessage doesn't)
1221
+ const channelName = `twitter_oauth_${state}`;
1222
+ let broadcastChannel = null;
1223
+ try {
1224
+ broadcastChannel = new BroadcastChannel(channelName);
1225
+ }
1226
+ catch (_a) {
1227
+ // BroadcastChannel not supported, fall back to postMessage only
1228
+ }
1130
1229
  const cleanup = (messageHandler, timeoutHandle, intervalHandle) => {
1131
1230
  window.removeEventListener('message', messageHandler);
1132
1231
  clearTimeout(timeoutHandle);
1133
1232
  clearInterval(intervalHandle);
1134
- };
1135
- const messageHandler = (event) => {
1136
- var _a, _b, _c, _d;
1137
- if (event.origin !== window.location.origin) {
1138
- return;
1233
+ if (broadcastChannel) {
1234
+ broadcastChannel.close();
1139
1235
  }
1140
- if (((_a = event.data) === null || _a === void 0 ? void 0 : _a.type) === 'oauth-response') {
1141
- if (((_b = event.data) === null || _b === void 0 ? void 0 : _b.provider) && event.data.provider !== 'twitter') {
1142
- return;
1236
+ };
1237
+ const handleOAuthMessage = (data) => {
1238
+ if ((data === null || data === void 0 ? void 0 : data.type) === 'oauth-response') {
1239
+ if ((data === null || data === void 0 ? void 0 : data.provider) && data.provider !== 'twitter') {
1240
+ return false;
1143
1241
  }
1144
1242
  cleanup(messageHandler, timeoutHandle, popupClosedInterval);
1145
1243
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
1146
- const _e = event.data, { provider: _ignoredProvider } = _e, payload = __rest(_e, ["provider"]);
1244
+ const _a = data, { provider: _ignoredProvider } = _a, payload = __rest(_a, ["provider"]);
1147
1245
  resolve({
1148
1246
  provider: 'twitter',
1149
1247
  result: payload,
1150
1248
  });
1249
+ return true;
1151
1250
  }
1152
- else if (((_c = event.data) === null || _c === void 0 ? void 0 : _c.type) === 'oauth-error') {
1153
- if (((_d = event.data) === null || _d === void 0 ? void 0 : _d.provider) && event.data.provider !== 'twitter') {
1154
- return;
1251
+ else if ((data === null || data === void 0 ? void 0 : data.type) === 'oauth-error') {
1252
+ if ((data === null || data === void 0 ? void 0 : data.provider) && data.provider !== 'twitter') {
1253
+ return false;
1155
1254
  }
1156
1255
  cleanup(messageHandler, timeoutHandle, popupClosedInterval);
1157
- reject(new Error(event.data.error || 'Twitter login was cancelled.'));
1256
+ reject(new Error(data.error || 'Twitter login was cancelled.'));
1257
+ return true;
1258
+ }
1259
+ return false;
1260
+ };
1261
+ // Listen for BroadcastChannel messages
1262
+ if (broadcastChannel) {
1263
+ broadcastChannel.onmessage = (event) => {
1264
+ handleOAuthMessage(event.data);
1265
+ };
1266
+ }
1267
+ const messageHandler = (event) => {
1268
+ if (event.origin !== window.location.origin) {
1269
+ return;
1158
1270
  }
1271
+ handleOAuthMessage(event.data);
1159
1272
  };
1160
1273
  window.addEventListener('message', messageHandler);
1161
1274
  const timeoutHandle = window.setTimeout(() => {
1162
- window.removeEventListener('message', messageHandler);
1163
- popup.close();
1275
+ cleanup(messageHandler, timeoutHandle, popupClosedInterval);
1276
+ try {
1277
+ popup.close();
1278
+ }
1279
+ catch (_a) {
1280
+ // Ignore cross-origin errors when closing
1281
+ }
1164
1282
  reject(new Error('Twitter login timed out.'));
1165
1283
  }, 300000);
1166
1284
  const popupClosedInterval = window.setInterval(() => {
1167
- if (popup.closed) {
1168
- window.removeEventListener('message', messageHandler);
1285
+ try {
1286
+ // Check if popup is closed - this may throw cross-origin errors for some providers
1287
+ if (popup.closed) {
1288
+ cleanup(messageHandler, timeoutHandle, popupClosedInterval);
1289
+ reject(new Error('Twitter login window was closed.'));
1290
+ }
1291
+ }
1292
+ catch (_a) {
1293
+ // Cross-origin error when checking popup.closed - this happens when the popup
1294
+ // navigates to a third-party OAuth provider with strict security settings.
1295
+ // We can't detect if the window was closed, so we rely on timeout and message handlers.
1169
1296
  clearInterval(popupClosedInterval);
1170
- clearTimeout(timeoutHandle);
1171
- reject(new Error('Twitter login window was closed.'));
1172
1297
  }
1173
1298
  }, 1000);
1174
1299
  });
@@ -1419,7 +1544,7 @@ class SocialLoginWeb extends core.WebPlugin {
1419
1544
  }
1420
1545
  }
1421
1546
  async handleOAuthRedirect() {
1422
- var _a, _b, _c;
1547
+ var _a;
1423
1548
  const url = new URL(window.location.href);
1424
1549
  const stateRaw = localStorage.getItem(SocialLoginWeb.OAUTH_STATE_KEY);
1425
1550
  let provider = null;
@@ -1430,7 +1555,7 @@ class SocialLoginWeb extends core.WebPlugin {
1430
1555
  provider = (_a = parsed.provider) !== null && _a !== void 0 ? _a : null;
1431
1556
  state = parsed.state;
1432
1557
  }
1433
- catch (_d) {
1558
+ catch (_b) {
1434
1559
  provider = stateRaw === 'true' ? 'google' : null;
1435
1560
  }
1436
1561
  }
@@ -1450,16 +1575,53 @@ class SocialLoginWeb extends core.WebPlugin {
1450
1575
  if (!result) {
1451
1576
  return;
1452
1577
  }
1578
+ // Build the message to send
1579
+ let message;
1453
1580
  if ('error' in result) {
1454
1581
  const resolvedProvider = provider !== null && provider !== void 0 ? provider : null;
1455
- (_b = window.opener) === null || _b === void 0 ? void 0 : _b.postMessage({
1582
+ message = {
1456
1583
  type: 'oauth-error',
1457
1584
  provider: resolvedProvider,
1458
1585
  error: result.error,
1459
- }, window.location.origin);
1586
+ };
1460
1587
  }
1461
1588
  else {
1462
- (_c = window.opener) === null || _c === void 0 ? void 0 : _c.postMessage(Object.assign({ type: 'oauth-response', provider: result.provider }, result.result), window.location.origin);
1589
+ message = Object.assign({ type: 'oauth-response', provider: result.provider }, result.result);
1590
+ }
1591
+ // Try postMessage first (works when window.opener is accessible)
1592
+ try {
1593
+ if (window.opener) {
1594
+ window.opener.postMessage(message, window.location.origin);
1595
+ }
1596
+ }
1597
+ catch (_c) {
1598
+ // Cross-origin error - window.opener may not be accessible
1599
+ console.log('postMessage to opener failed, using BroadcastChannel');
1600
+ }
1601
+ // Also use BroadcastChannel as a fallback (works across same-origin windows
1602
+ // even when window.opener is not accessible due to cross-origin navigation)
1603
+ try {
1604
+ // Determine the channel name based on provider and state
1605
+ let channelName = null;
1606
+ if (provider === 'oauth2' && state) {
1607
+ channelName = `oauth2_${state}`;
1608
+ }
1609
+ else if (provider === 'twitter' && state) {
1610
+ channelName = `twitter_oauth_${state}`;
1611
+ }
1612
+ else if (provider === 'google') {
1613
+ // Google uses nonce which we can't easily recover here, but try anyway
1614
+ // The parent window will have created a channel with a nonce-based name
1615
+ }
1616
+ if (channelName) {
1617
+ const channel = new BroadcastChannel(channelName);
1618
+ channel.postMessage(message);
1619
+ channel.close();
1620
+ }
1621
+ }
1622
+ catch (_d) {
1623
+ // BroadcastChannel not supported or other error
1624
+ console.log('BroadcastChannel not available');
1463
1625
  }
1464
1626
  window.close();
1465
1627
  }