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