@capgo/capacitor-social-login 7.15.2 → 7.17.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 +55 -18
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/ee/forgr/capacitor/social/login/AppleProvider.java +1 -1
- package/android/src/main/java/ee/forgr/capacitor/social/login/SocialLoginPlugin.java +32 -1
- package/android/src/main/java/ee/forgr/capacitor/social/login/TwitterLoginActivity.java +93 -0
- package/android/src/main/java/ee/forgr/capacitor/social/login/TwitterProvider.java +510 -0
- package/dist/docs.json +184 -8
- package/dist/esm/definitions.d.ts +83 -3
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/google-provider.d.ts +3 -1
- package/dist/esm/google-provider.js +25 -3
- package/dist/esm/google-provider.js.map +1 -1
- package/dist/esm/twitter-provider.d.ts +36 -0
- package/dist/esm/twitter-provider.js +346 -0
- package/dist/esm/twitter-provider.js.map +1 -0
- package/dist/esm/web.d.ts +2 -1
- package/dist/esm/web.js +59 -8
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +428 -11
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +428 -11
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/SocialLoginPlugin/SocialLoginPlugin.swift +93 -1
- package/ios/Sources/SocialLoginPlugin/TwitterProvider.swift +381 -0
- package/package.json +1 -1
package/dist/plugin.js
CHANGED
|
@@ -350,6 +350,13 @@ var capacitorCapacitorUpdater = (function (exports, core) {
|
|
|
350
350
|
}
|
|
351
351
|
handleOAuthRedirect(url) {
|
|
352
352
|
const paramsRaw = url.searchParams;
|
|
353
|
+
// Check for errors in search params first (for offline mode)
|
|
354
|
+
const errorInParams = paramsRaw.get('error');
|
|
355
|
+
if (errorInParams) {
|
|
356
|
+
localStorage.removeItem(BaseSocialLogin.OAUTH_STATE_KEY);
|
|
357
|
+
const errorDescription = paramsRaw.get('error_description') || errorInParams;
|
|
358
|
+
return { error: errorDescription };
|
|
359
|
+
}
|
|
353
360
|
const code = paramsRaw.get('code');
|
|
354
361
|
if (code && paramsRaw.has('scope')) {
|
|
355
362
|
return {
|
|
@@ -364,8 +371,15 @@ var capacitorCapacitorUpdater = (function (exports, core) {
|
|
|
364
371
|
console.log('handleOAuthRedirect', url.hash);
|
|
365
372
|
if (!hash)
|
|
366
373
|
return null;
|
|
367
|
-
console.log('handleOAuthRedirect ok');
|
|
368
374
|
const params = new URLSearchParams(hash);
|
|
375
|
+
// Check for error cases in hash (e.g., user cancelled)
|
|
376
|
+
const error = params.get('error');
|
|
377
|
+
if (error) {
|
|
378
|
+
localStorage.removeItem(BaseSocialLogin.OAUTH_STATE_KEY);
|
|
379
|
+
const errorDescription = params.get('error_description') || error;
|
|
380
|
+
return { error: errorDescription };
|
|
381
|
+
}
|
|
382
|
+
console.log('handleOAuthRedirect ok');
|
|
369
383
|
const accessToken = params.get('access_token');
|
|
370
384
|
const idToken = params.get('id_token');
|
|
371
385
|
if (accessToken && idToken) {
|
|
@@ -516,7 +530,7 @@ var capacitorCapacitorUpdater = (function (exports, core) {
|
|
|
516
530
|
const height = 600;
|
|
517
531
|
const left = window.screenX + (window.outerWidth - width) / 2;
|
|
518
532
|
const top = window.screenY + (window.outerHeight - height) / 2;
|
|
519
|
-
localStorage.setItem(BaseSocialLogin.OAUTH_STATE_KEY, '
|
|
533
|
+
localStorage.setItem(BaseSocialLogin.OAUTH_STATE_KEY, JSON.stringify({ provider: 'google', loginType: this.loginType }));
|
|
520
534
|
const popup = window.open(url, 'Google Sign In', `width=${width},height=${height},left=${left},top=${top},popup=1`);
|
|
521
535
|
let popupClosedInterval;
|
|
522
536
|
let timeoutHandle;
|
|
@@ -527,12 +541,13 @@ var capacitorCapacitorUpdater = (function (exports, core) {
|
|
|
527
541
|
return;
|
|
528
542
|
}
|
|
529
543
|
const handleMessage = (event) => {
|
|
530
|
-
var _a, _b, _c;
|
|
544
|
+
var _a, _b, _c, _d;
|
|
531
545
|
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')))
|
|
532
546
|
return;
|
|
533
547
|
if (((_c = event.data) === null || _c === void 0 ? void 0 : _c.type) === 'oauth-response') {
|
|
534
548
|
window.removeEventListener('message', handleMessage);
|
|
535
549
|
clearInterval(popupClosedInterval);
|
|
550
|
+
clearTimeout(timeoutHandle);
|
|
536
551
|
if (this.loginType === 'online') {
|
|
537
552
|
const { accessToken, idToken } = event.data;
|
|
538
553
|
if (accessToken && idToken) {
|
|
@@ -569,6 +584,13 @@ var capacitorCapacitorUpdater = (function (exports, core) {
|
|
|
569
584
|
});
|
|
570
585
|
}
|
|
571
586
|
}
|
|
587
|
+
else if (((_d = event.data) === null || _d === void 0 ? void 0 : _d.type) === 'oauth-error') {
|
|
588
|
+
window.removeEventListener('message', handleMessage);
|
|
589
|
+
clearInterval(popupClosedInterval);
|
|
590
|
+
clearTimeout(timeoutHandle);
|
|
591
|
+
const errorMessage = event.data.error || 'User cancelled the OAuth flow';
|
|
592
|
+
reject(new Error(errorMessage));
|
|
593
|
+
}
|
|
572
594
|
// Don't reject for non-OAuth messages, just ignore them
|
|
573
595
|
};
|
|
574
596
|
window.addEventListener('message', handleMessage);
|
|
@@ -589,29 +611,411 @@ var capacitorCapacitorUpdater = (function (exports, core) {
|
|
|
589
611
|
}
|
|
590
612
|
}
|
|
591
613
|
|
|
592
|
-
|
|
614
|
+
var __rest = (undefined && undefined.__rest) || function (s, e) {
|
|
615
|
+
var t = {};
|
|
616
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
617
|
+
t[p] = s[p];
|
|
618
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
619
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
620
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
621
|
+
t[p[i]] = s[p[i]];
|
|
622
|
+
}
|
|
623
|
+
return t;
|
|
624
|
+
};
|
|
625
|
+
class TwitterSocialLogin extends BaseSocialLogin {
|
|
593
626
|
constructor() {
|
|
627
|
+
super(...arguments);
|
|
628
|
+
this.clientId = null;
|
|
629
|
+
this.redirectUrl = null;
|
|
630
|
+
this.defaultScopes = ['tweet.read', 'users.read'];
|
|
631
|
+
this.forceLogin = false;
|
|
632
|
+
this.TOKENS_KEY = 'capgo_social_login_twitter_tokens_v1';
|
|
633
|
+
this.STATE_PREFIX = 'capgo_social_login_twitter_state_';
|
|
634
|
+
}
|
|
635
|
+
async initialize(clientId, redirectUrl, defaultScopes, forceLogin, audience) {
|
|
636
|
+
this.clientId = clientId;
|
|
637
|
+
this.redirectUrl = redirectUrl !== null && redirectUrl !== void 0 ? redirectUrl : null;
|
|
638
|
+
if (defaultScopes === null || defaultScopes === void 0 ? void 0 : defaultScopes.length) {
|
|
639
|
+
this.defaultScopes = defaultScopes;
|
|
640
|
+
}
|
|
641
|
+
this.forceLogin = forceLogin !== null && forceLogin !== void 0 ? forceLogin : false;
|
|
642
|
+
this.audience = audience !== null && audience !== void 0 ? audience : undefined;
|
|
643
|
+
}
|
|
644
|
+
async login(options) {
|
|
645
|
+
var _a, _b, _c, _d, _e, _f;
|
|
646
|
+
if (!this.clientId) {
|
|
647
|
+
throw new Error('Twitter Client ID not configured. Call initialize() first.');
|
|
648
|
+
}
|
|
649
|
+
const redirectUri = (_b = (_a = options.redirectUrl) !== null && _a !== void 0 ? _a : this.redirectUrl) !== null && _b !== void 0 ? _b : window.location.origin + window.location.pathname;
|
|
650
|
+
const scopes = ((_c = options.scopes) === null || _c === void 0 ? void 0 : _c.length) ? options.scopes : this.defaultScopes;
|
|
651
|
+
const state = (_d = options.state) !== null && _d !== void 0 ? _d : this.generateState();
|
|
652
|
+
const codeVerifier = (_e = options.codeVerifier) !== null && _e !== void 0 ? _e : this.generateCodeVerifier();
|
|
653
|
+
const codeChallenge = await this.generateCodeChallenge(codeVerifier);
|
|
654
|
+
this.persistPendingLogin(state, {
|
|
655
|
+
codeVerifier,
|
|
656
|
+
redirectUri,
|
|
657
|
+
scopes,
|
|
658
|
+
});
|
|
659
|
+
localStorage.setItem(BaseSocialLogin.OAUTH_STATE_KEY, JSON.stringify({ provider: 'twitter', state }));
|
|
660
|
+
const params = new URLSearchParams({
|
|
661
|
+
response_type: 'code',
|
|
662
|
+
client_id: this.clientId,
|
|
663
|
+
redirect_uri: redirectUri,
|
|
664
|
+
scope: scopes.join(' '),
|
|
665
|
+
state,
|
|
666
|
+
code_challenge: codeChallenge,
|
|
667
|
+
code_challenge_method: 'S256',
|
|
668
|
+
});
|
|
669
|
+
if (((_f = options.forceLogin) !== null && _f !== void 0 ? _f : this.forceLogin) === true) {
|
|
670
|
+
params.set('force_login', 'true');
|
|
671
|
+
}
|
|
672
|
+
if (this.audience) {
|
|
673
|
+
params.set('audience', this.audience);
|
|
674
|
+
}
|
|
675
|
+
const authUrl = `https://x.com/i/oauth2/authorize?${params.toString()}`;
|
|
676
|
+
const width = 500;
|
|
677
|
+
const height = 650;
|
|
678
|
+
const left = window.screenX + (window.outerWidth - width) / 2;
|
|
679
|
+
const top = window.screenY + (window.outerHeight - height) / 2;
|
|
680
|
+
const popup = window.open(authUrl, 'XLogin', `width=${width},height=${height},left=${left},top=${top},popup=1`);
|
|
681
|
+
return new Promise((resolve, reject) => {
|
|
682
|
+
if (!popup) {
|
|
683
|
+
reject(new Error('Unable to open login window. Please allow popups.'));
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
const cleanup = (messageHandler, timeoutHandle, intervalHandle) => {
|
|
687
|
+
window.removeEventListener('message', messageHandler);
|
|
688
|
+
clearTimeout(timeoutHandle);
|
|
689
|
+
clearInterval(intervalHandle);
|
|
690
|
+
};
|
|
691
|
+
const messageHandler = (event) => {
|
|
692
|
+
var _a, _b, _c, _d;
|
|
693
|
+
if (event.origin !== window.location.origin) {
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
if (((_a = event.data) === null || _a === void 0 ? void 0 : _a.type) === 'oauth-response') {
|
|
697
|
+
if (((_b = event.data) === null || _b === void 0 ? void 0 : _b.provider) && event.data.provider !== 'twitter') {
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
cleanup(messageHandler, timeoutHandle, popupClosedInterval);
|
|
701
|
+
const _e = event.data, { provider: _ignoredProvider } = _e, payload = __rest(_e, ["provider"]);
|
|
702
|
+
resolve({
|
|
703
|
+
provider: 'twitter',
|
|
704
|
+
result: payload,
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
else if (((_c = event.data) === null || _c === void 0 ? void 0 : _c.type) === 'oauth-error') {
|
|
708
|
+
if (((_d = event.data) === null || _d === void 0 ? void 0 : _d.provider) && event.data.provider !== 'twitter') {
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
cleanup(messageHandler, timeoutHandle, popupClosedInterval);
|
|
712
|
+
reject(new Error(event.data.error || 'Twitter login was cancelled.'));
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
window.addEventListener('message', messageHandler);
|
|
716
|
+
const timeoutHandle = window.setTimeout(() => {
|
|
717
|
+
window.removeEventListener('message', messageHandler);
|
|
718
|
+
popup.close();
|
|
719
|
+
reject(new Error('Twitter login timed out.'));
|
|
720
|
+
}, 300000);
|
|
721
|
+
const popupClosedInterval = window.setInterval(() => {
|
|
722
|
+
if (popup.closed) {
|
|
723
|
+
window.removeEventListener('message', messageHandler);
|
|
724
|
+
clearInterval(popupClosedInterval);
|
|
725
|
+
clearTimeout(timeoutHandle);
|
|
726
|
+
reject(new Error('Twitter login window was closed.'));
|
|
727
|
+
}
|
|
728
|
+
}, 1000);
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
async logout() {
|
|
732
|
+
localStorage.removeItem(this.TOKENS_KEY);
|
|
733
|
+
}
|
|
734
|
+
async isLoggedIn() {
|
|
735
|
+
const tokens = this.getStoredTokens();
|
|
736
|
+
if (!tokens) {
|
|
737
|
+
return { isLoggedIn: false };
|
|
738
|
+
}
|
|
739
|
+
const isValid = tokens.expiresAt > Date.now();
|
|
740
|
+
if (!isValid) {
|
|
741
|
+
localStorage.removeItem(this.TOKENS_KEY);
|
|
742
|
+
}
|
|
743
|
+
return { isLoggedIn: isValid };
|
|
744
|
+
}
|
|
745
|
+
async getAuthorizationCode() {
|
|
746
|
+
const tokens = this.getStoredTokens();
|
|
747
|
+
if (!tokens) {
|
|
748
|
+
throw new Error('Twitter access token is not available.');
|
|
749
|
+
}
|
|
750
|
+
return {
|
|
751
|
+
accessToken: tokens.accessToken,
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
async refresh() {
|
|
755
|
+
const tokens = this.getStoredTokens();
|
|
756
|
+
if (!(tokens === null || tokens === void 0 ? void 0 : tokens.refreshToken)) {
|
|
757
|
+
throw new Error('No Twitter refresh token is available. Include offline.access scope to receive one.');
|
|
758
|
+
}
|
|
759
|
+
await this.refreshWithRefreshToken(tokens.refreshToken);
|
|
760
|
+
}
|
|
761
|
+
async handleOAuthRedirect(url, expectedState) {
|
|
762
|
+
const params = url.searchParams;
|
|
763
|
+
const stateFromUrl = expectedState !== null && expectedState !== void 0 ? expectedState : params.get('state');
|
|
764
|
+
if (!stateFromUrl) {
|
|
765
|
+
return null;
|
|
766
|
+
}
|
|
767
|
+
const pending = this.consumePendingLogin(stateFromUrl);
|
|
768
|
+
if (!pending) {
|
|
769
|
+
localStorage.removeItem(BaseSocialLogin.OAUTH_STATE_KEY);
|
|
770
|
+
return { error: 'Twitter login session expired or state mismatch.' };
|
|
771
|
+
}
|
|
772
|
+
const error = params.get('error');
|
|
773
|
+
if (error) {
|
|
774
|
+
localStorage.removeItem(BaseSocialLogin.OAUTH_STATE_KEY);
|
|
775
|
+
return { error: params.get('error_description') || error };
|
|
776
|
+
}
|
|
777
|
+
const code = params.get('code');
|
|
778
|
+
if (!code) {
|
|
779
|
+
localStorage.removeItem(BaseSocialLogin.OAUTH_STATE_KEY);
|
|
780
|
+
return { error: 'Twitter authorization code missing from redirect.' };
|
|
781
|
+
}
|
|
782
|
+
try {
|
|
783
|
+
const tokens = await this.exchangeAuthorizationCode(code, pending);
|
|
784
|
+
const profile = await this.fetchProfile(tokens.access_token);
|
|
785
|
+
const expiresAt = Date.now() + tokens.expires_in * 1000;
|
|
786
|
+
const scopeArray = tokens.scope.split(' ').filter(Boolean);
|
|
787
|
+
this.persistTokens({
|
|
788
|
+
accessToken: tokens.access_token,
|
|
789
|
+
refreshToken: tokens.refresh_token,
|
|
790
|
+
expiresAt,
|
|
791
|
+
scope: scopeArray,
|
|
792
|
+
tokenType: tokens.token_type,
|
|
793
|
+
userId: profile.id,
|
|
794
|
+
profile,
|
|
795
|
+
});
|
|
796
|
+
return {
|
|
797
|
+
provider: 'twitter',
|
|
798
|
+
result: {
|
|
799
|
+
accessToken: {
|
|
800
|
+
token: tokens.access_token,
|
|
801
|
+
tokenType: tokens.token_type,
|
|
802
|
+
expires: new Date(expiresAt).toISOString(),
|
|
803
|
+
userId: profile.id,
|
|
804
|
+
},
|
|
805
|
+
refreshToken: tokens.refresh_token,
|
|
806
|
+
scope: scopeArray,
|
|
807
|
+
tokenType: tokens.token_type,
|
|
808
|
+
expiresIn: tokens.expires_in,
|
|
809
|
+
profile,
|
|
810
|
+
},
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
catch (err) {
|
|
814
|
+
if (err instanceof Error) {
|
|
815
|
+
return { error: err.message };
|
|
816
|
+
}
|
|
817
|
+
return { error: 'Twitter login failed unexpectedly.' };
|
|
818
|
+
}
|
|
819
|
+
finally {
|
|
820
|
+
localStorage.removeItem(BaseSocialLogin.OAUTH_STATE_KEY);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
async exchangeAuthorizationCode(code, pending) {
|
|
594
824
|
var _a;
|
|
825
|
+
const params = new URLSearchParams({
|
|
826
|
+
grant_type: 'authorization_code',
|
|
827
|
+
client_id: (_a = this.clientId) !== null && _a !== void 0 ? _a : '',
|
|
828
|
+
code,
|
|
829
|
+
redirect_uri: pending.redirectUri,
|
|
830
|
+
code_verifier: pending.codeVerifier,
|
|
831
|
+
});
|
|
832
|
+
const response = await fetch('https://api.x.com/2/oauth2/token', {
|
|
833
|
+
method: 'POST',
|
|
834
|
+
headers: {
|
|
835
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
836
|
+
},
|
|
837
|
+
body: params.toString(),
|
|
838
|
+
});
|
|
839
|
+
if (!response.ok) {
|
|
840
|
+
const text = await response.text();
|
|
841
|
+
throw new Error(`Twitter token exchange failed (${response.status}): ${text}`);
|
|
842
|
+
}
|
|
843
|
+
return (await response.json());
|
|
844
|
+
}
|
|
845
|
+
async refreshWithRefreshToken(refreshToken) {
|
|
846
|
+
var _a, _b;
|
|
847
|
+
const params = new URLSearchParams({
|
|
848
|
+
grant_type: 'refresh_token',
|
|
849
|
+
refresh_token: refreshToken,
|
|
850
|
+
client_id: (_a = this.clientId) !== null && _a !== void 0 ? _a : '',
|
|
851
|
+
});
|
|
852
|
+
const response = await fetch('https://api.x.com/2/oauth2/token', {
|
|
853
|
+
method: 'POST',
|
|
854
|
+
headers: {
|
|
855
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
856
|
+
},
|
|
857
|
+
body: params.toString(),
|
|
858
|
+
});
|
|
859
|
+
if (!response.ok) {
|
|
860
|
+
const text = await response.text();
|
|
861
|
+
throw new Error(`Twitter refresh failed (${response.status}): ${text}`);
|
|
862
|
+
}
|
|
863
|
+
const tokens = (await response.json());
|
|
864
|
+
const profile = await this.fetchProfile(tokens.access_token);
|
|
865
|
+
const expiresAt = Date.now() + tokens.expires_in * 1000;
|
|
866
|
+
const scopeArray = tokens.scope.split(' ').filter(Boolean);
|
|
867
|
+
this.persistTokens({
|
|
868
|
+
accessToken: tokens.access_token,
|
|
869
|
+
refreshToken: (_b = tokens.refresh_token) !== null && _b !== void 0 ? _b : refreshToken,
|
|
870
|
+
expiresAt,
|
|
871
|
+
scope: scopeArray,
|
|
872
|
+
tokenType: tokens.token_type,
|
|
873
|
+
userId: profile.id,
|
|
874
|
+
profile,
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
async fetchProfile(accessToken) {
|
|
878
|
+
var _a, _b, _c, _d;
|
|
879
|
+
const fields = ['profile_image_url', 'verified', 'name', 'username'];
|
|
880
|
+
const response = await fetch(`https://api.x.com/2/users/me?user.fields=${fields.join(',')}`, {
|
|
881
|
+
headers: {
|
|
882
|
+
Authorization: `Bearer ${accessToken}`,
|
|
883
|
+
},
|
|
884
|
+
});
|
|
885
|
+
if (!response.ok) {
|
|
886
|
+
const text = await response.text();
|
|
887
|
+
throw new Error(`Unable to fetch Twitter profile (${response.status}): ${text}`);
|
|
888
|
+
}
|
|
889
|
+
const payload = (await response.json());
|
|
890
|
+
if (!payload.data) {
|
|
891
|
+
throw new Error('Twitter profile payload is missing data.');
|
|
892
|
+
}
|
|
893
|
+
return {
|
|
894
|
+
id: payload.data.id,
|
|
895
|
+
username: payload.data.username,
|
|
896
|
+
name: (_a = payload.data.name) !== null && _a !== void 0 ? _a : null,
|
|
897
|
+
profileImageUrl: (_b = payload.data.profile_image_url) !== null && _b !== void 0 ? _b : null,
|
|
898
|
+
verified: (_c = payload.data.verified) !== null && _c !== void 0 ? _c : false,
|
|
899
|
+
email: (_d = payload.data.email) !== null && _d !== void 0 ? _d : null,
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
persistTokens(tokens) {
|
|
903
|
+
localStorage.setItem(this.TOKENS_KEY, JSON.stringify(tokens));
|
|
904
|
+
}
|
|
905
|
+
getStoredTokens() {
|
|
906
|
+
const raw = localStorage.getItem(this.TOKENS_KEY);
|
|
907
|
+
if (!raw) {
|
|
908
|
+
return null;
|
|
909
|
+
}
|
|
910
|
+
try {
|
|
911
|
+
return JSON.parse(raw);
|
|
912
|
+
}
|
|
913
|
+
catch (err) {
|
|
914
|
+
console.warn('Failed to parse stored Twitter tokens', err);
|
|
915
|
+
return null;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
persistPendingLogin(state, payload) {
|
|
919
|
+
localStorage.setItem(`${this.STATE_PREFIX}${state}`, JSON.stringify(payload));
|
|
920
|
+
}
|
|
921
|
+
consumePendingLogin(state) {
|
|
922
|
+
const key = `${this.STATE_PREFIX}${state}`;
|
|
923
|
+
const raw = localStorage.getItem(key);
|
|
924
|
+
localStorage.removeItem(key);
|
|
925
|
+
if (!raw) {
|
|
926
|
+
return null;
|
|
927
|
+
}
|
|
928
|
+
try {
|
|
929
|
+
return JSON.parse(raw);
|
|
930
|
+
}
|
|
931
|
+
catch (err) {
|
|
932
|
+
console.warn('Failed to parse pending Twitter login payload', err);
|
|
933
|
+
return null;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
generateState() {
|
|
937
|
+
return [...crypto.getRandomValues(new Uint8Array(16))].map((b) => b.toString(16).padStart(2, '0')).join('');
|
|
938
|
+
}
|
|
939
|
+
generateCodeVerifier() {
|
|
940
|
+
const array = new Uint8Array(64);
|
|
941
|
+
crypto.getRandomValues(array);
|
|
942
|
+
return Array.from(array)
|
|
943
|
+
.map((b) => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~'[b % 66])
|
|
944
|
+
.join('');
|
|
945
|
+
}
|
|
946
|
+
async generateCodeChallenge(codeVerifier) {
|
|
947
|
+
const encoder = new TextEncoder();
|
|
948
|
+
const data = encoder.encode(codeVerifier);
|
|
949
|
+
const digest = await crypto.subtle.digest('SHA-256', data);
|
|
950
|
+
return this.base64UrlEncode(new Uint8Array(digest));
|
|
951
|
+
}
|
|
952
|
+
base64UrlEncode(buffer) {
|
|
953
|
+
let binary = '';
|
|
954
|
+
buffer.forEach((b) => (binary += String.fromCharCode(b)));
|
|
955
|
+
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
class SocialLoginWeb extends core.WebPlugin {
|
|
960
|
+
constructor() {
|
|
595
961
|
super();
|
|
596
962
|
this.googleProvider = new GoogleSocialLogin();
|
|
597
963
|
this.appleProvider = new AppleSocialLogin();
|
|
598
964
|
this.facebookProvider = new FacebookSocialLogin();
|
|
965
|
+
this.twitterProvider = new TwitterSocialLogin();
|
|
599
966
|
// Set up listener for OAuth redirects if we have a pending OAuth flow
|
|
600
967
|
if (localStorage.getItem(SocialLoginWeb.OAUTH_STATE_KEY)) {
|
|
601
968
|
console.log('OAUTH_STATE_KEY found');
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
(_a = window.opener) === null || _a === void 0 ? void 0 : _a.postMessage(Object.assign({ type: 'oauth-response' }, result.result), window.location.origin);
|
|
969
|
+
this.handleOAuthRedirect().catch((error) => {
|
|
970
|
+
console.error('Failed to finish OAuth redirect', error);
|
|
605
971
|
window.close();
|
|
606
|
-
}
|
|
972
|
+
});
|
|
607
973
|
}
|
|
608
974
|
}
|
|
609
|
-
handleOAuthRedirect() {
|
|
975
|
+
async handleOAuthRedirect() {
|
|
976
|
+
var _a, _b, _c;
|
|
610
977
|
const url = new URL(window.location.href);
|
|
611
|
-
|
|
978
|
+
const stateRaw = localStorage.getItem(SocialLoginWeb.OAUTH_STATE_KEY);
|
|
979
|
+
let provider = null;
|
|
980
|
+
let state;
|
|
981
|
+
if (stateRaw) {
|
|
982
|
+
try {
|
|
983
|
+
const parsed = JSON.parse(stateRaw);
|
|
984
|
+
provider = (_a = parsed.provider) !== null && _a !== void 0 ? _a : null;
|
|
985
|
+
state = parsed.state;
|
|
986
|
+
}
|
|
987
|
+
catch (_d) {
|
|
988
|
+
provider = stateRaw === 'true' ? 'google' : null;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
let result = null;
|
|
992
|
+
switch (provider) {
|
|
993
|
+
case 'twitter':
|
|
994
|
+
result = await this.twitterProvider.handleOAuthRedirect(url, state);
|
|
995
|
+
break;
|
|
996
|
+
case 'google':
|
|
997
|
+
default:
|
|
998
|
+
result = this.googleProvider.handleOAuthRedirect(url);
|
|
999
|
+
break;
|
|
1000
|
+
}
|
|
1001
|
+
if (!result) {
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
if ('error' in result) {
|
|
1005
|
+
const resolvedProvider = provider !== null && provider !== void 0 ? provider : null;
|
|
1006
|
+
(_b = window.opener) === null || _b === void 0 ? void 0 : _b.postMessage({
|
|
1007
|
+
type: 'oauth-error',
|
|
1008
|
+
provider: resolvedProvider,
|
|
1009
|
+
error: result.error,
|
|
1010
|
+
}, window.location.origin);
|
|
1011
|
+
}
|
|
1012
|
+
else {
|
|
1013
|
+
(_c = window.opener) === null || _c === void 0 ? void 0 : _c.postMessage(Object.assign({ type: 'oauth-response', provider: result.provider }, result.result), window.location.origin);
|
|
1014
|
+
}
|
|
1015
|
+
window.close();
|
|
612
1016
|
}
|
|
613
1017
|
async initialize(options) {
|
|
614
|
-
var _a, _b, _c;
|
|
1018
|
+
var _a, _b, _c, _d;
|
|
615
1019
|
const initPromises = [];
|
|
616
1020
|
if ((_a = options.google) === null || _a === void 0 ? void 0 : _a.webClientId) {
|
|
617
1021
|
initPromises.push(this.googleProvider.initialize(options.google.webClientId, options.google.mode, options.google.hostedDomain, options.google.redirectUrl));
|
|
@@ -622,6 +1026,9 @@ var capacitorCapacitorUpdater = (function (exports, core) {
|
|
|
622
1026
|
if ((_c = options.facebook) === null || _c === void 0 ? void 0 : _c.appId) {
|
|
623
1027
|
initPromises.push(this.facebookProvider.initialize(options.facebook.appId, options.facebook.locale));
|
|
624
1028
|
}
|
|
1029
|
+
if ((_d = options.twitter) === null || _d === void 0 ? void 0 : _d.clientId) {
|
|
1030
|
+
initPromises.push(this.twitterProvider.initialize(options.twitter.clientId, options.twitter.redirectUrl, options.twitter.defaultScopes, options.twitter.forceLogin, options.twitter.audience));
|
|
1031
|
+
}
|
|
625
1032
|
await Promise.all(initPromises);
|
|
626
1033
|
}
|
|
627
1034
|
async login(options) {
|
|
@@ -632,6 +1039,8 @@ var capacitorCapacitorUpdater = (function (exports, core) {
|
|
|
632
1039
|
return this.appleProvider.login(options.options);
|
|
633
1040
|
case 'facebook':
|
|
634
1041
|
return this.facebookProvider.login(options.options);
|
|
1042
|
+
case 'twitter':
|
|
1043
|
+
return this.twitterProvider.login(options.options);
|
|
635
1044
|
default:
|
|
636
1045
|
throw new Error(`Login for ${options.provider} is not implemented on web`);
|
|
637
1046
|
}
|
|
@@ -644,6 +1053,8 @@ var capacitorCapacitorUpdater = (function (exports, core) {
|
|
|
644
1053
|
return this.appleProvider.logout();
|
|
645
1054
|
case 'facebook':
|
|
646
1055
|
return this.facebookProvider.logout();
|
|
1056
|
+
case 'twitter':
|
|
1057
|
+
return this.twitterProvider.logout();
|
|
647
1058
|
default:
|
|
648
1059
|
throw new Error(`Logout for ${options.provider} is not implemented`);
|
|
649
1060
|
}
|
|
@@ -656,6 +1067,8 @@ var capacitorCapacitorUpdater = (function (exports, core) {
|
|
|
656
1067
|
return this.appleProvider.isLoggedIn();
|
|
657
1068
|
case 'facebook':
|
|
658
1069
|
return this.facebookProvider.isLoggedIn();
|
|
1070
|
+
case 'twitter':
|
|
1071
|
+
return this.twitterProvider.isLoggedIn();
|
|
659
1072
|
default:
|
|
660
1073
|
throw new Error(`isLoggedIn for ${options.provider} is not implemented`);
|
|
661
1074
|
}
|
|
@@ -668,6 +1081,8 @@ var capacitorCapacitorUpdater = (function (exports, core) {
|
|
|
668
1081
|
return this.appleProvider.getAuthorizationCode();
|
|
669
1082
|
case 'facebook':
|
|
670
1083
|
return this.facebookProvider.getAuthorizationCode();
|
|
1084
|
+
case 'twitter':
|
|
1085
|
+
return this.twitterProvider.getAuthorizationCode();
|
|
671
1086
|
default:
|
|
672
1087
|
throw new Error(`getAuthorizationCode for ${options.provider} is not implemented`);
|
|
673
1088
|
}
|
|
@@ -680,6 +1095,8 @@ var capacitorCapacitorUpdater = (function (exports, core) {
|
|
|
680
1095
|
return this.appleProvider.refresh();
|
|
681
1096
|
case 'facebook':
|
|
682
1097
|
return this.facebookProvider.refresh(options.options);
|
|
1098
|
+
case 'twitter':
|
|
1099
|
+
return this.twitterProvider.refresh();
|
|
683
1100
|
default:
|
|
684
1101
|
throw new Error(`Refresh for ${options.provider} is not implemented`);
|
|
685
1102
|
}
|