diff --git a/docs/working/SECURE_CERTIFICATE.md b/docs/working/SECURE_CERTIFICATE.md index b0d4fb849..78505994f 100644 --- a/docs/working/SECURE_CERTIFICATE.md +++ b/docs/working/SECURE_CERTIFICATE.md @@ -38,12 +38,17 @@ In WebApp/src/js/config.js, set SECURE_CERTIFICATE_INSTALLED to true, and then u WebApp will startup at http://localhost:3000 -## Signing in with Facebook on your dev machine +## Signing in with Facebook and Twitter on your dev machine ### Make a small necessary change to your /etc/hosts Facebook will no longer redirect to localhost, so we make a second alias for 127.0.0.1 with this specific made up domain: `wevotedeveloper.com` by running the following command in a terminal: +This `wevotedeveloper.com` is also needed to debug "Sign in with Twitter" while using the `wevotedeveloper.com` domain for your local Python API server. +NOTE: As of March 2024, Chrome stopped responding to this domain (possibly since it can't match it with a public DNS lookup), so you will have to debug this setup with Safari. +Safari has a version of Devtools called "Web Inspector" that you get to by right-clicking in the tab, and clicking "Inspect Element". Devtools and Web +Inspector share the Chromium code base and have almost identical capabilities. + `sudo node node/updateHosts.js` The run will look like this diff --git a/src/App.jsx b/src/App.jsx index 296359fb8..2efd8333f 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -566,6 +566,7 @@ class App extends Component { + } /> diff --git a/src/js/actions/TwitterActions.js b/src/js/actions/TwitterActions.js index 43270c2de..b876935eb 100644 --- a/src/js/actions/TwitterActions.js +++ b/src/js/actions/TwitterActions.js @@ -20,14 +20,6 @@ export default { }); }, - twitterNativeSignInSave (twitterAccessToken, twitterAccessTokenSecret) { - Dispatcher.loadEndpoint('twitterNativeSignInSave', - { - twitter_access_token: twitterAccessToken, - twitter_access_token_secret: twitterAccessTokenSecret, - }); - }, - twitterProcessDeferredImages (twitterImageLoadInfo) { Dispatcher.loadEndpoint('twitterProcessDeferredImages', { status: twitterImageLoadInfo.status, @@ -54,4 +46,13 @@ export default { }); }, + twitterOauth1UserHandler (oauthToken, oauthVerifier) { // For twitter V2 API, March 2024 + // console.log('twitterOauth1UserHandler: ', oauthToken, oauthVerifier); + Dispatcher.loadEndpoint('twitterOauth1UserHandler', + { + oauth_token: oauthToken, + oauth_verifier: oauthVerifier, + }); + }, + }; diff --git a/src/js/components/Twitter/TwitterSignIn.jsx b/src/js/components/Twitter/TwitterSignIn.jsx index a4d2e8fcb..0690de309 100644 --- a/src/js/components/Twitter/TwitterSignIn.jsx +++ b/src/js/components/Twitter/TwitterSignIn.jsx @@ -83,6 +83,7 @@ class TwitterSignIn extends Component { twitterSignInCrash: false, twitterSignInStartSubmitted: false, }; + oAuthLog(`twitterSignIn redirect url = ${returnURL}`); } componentDidMount () { diff --git a/src/js/config - wevotedeveloper SSL Web and local API.js b/src/js/config - wevotedeveloper SSL Web and local API.js new file mode 100644 index 000000000..22c2be447 --- /dev/null +++ b/src/js/config - wevotedeveloper SSL Web and local API.js @@ -0,0 +1,53 @@ +/* eslint-disable */ +// Note that we import these values where needed as "webAppConfig" +module.exports = { + WE_VOTE_URL_PROTOCOL: 'https://', // "http://" for local dev or "https://" for live server + WE_VOTE_HOSTNAME: 'wevotedeveloper.com:3000', // This should be without "http...". This is "WeVote.US" on live server. + WE_VOTE_IMAGE_PATH_FOR_CORDOVA: 'https://wevote.us', // If you are not working with Cordova, you don't need to change this + SECURE_CERTIFICATE_INSTALLED: false, + + WE_VOTE_SERVER_ROOT_URL: 'https://wevotedeveloper.com:8000/', + WE_VOTE_SERVER_ADMIN_ROOT_URL: 'https://wevotedeveloper.com:8000/admin/', + WE_VOTE_SERVER_API_ROOT_URL: 'https://wevotedeveloper.com:8000/apis/v1/', + WE_VOTE_SERVER_API_CDN_ROOT_URL: 'https://wevotedeveloper.com:8000/apis/v1/', + + ENABLE_NEXT_RELEASE_FEATURES: false, + ENABLE_WORKBOX_SERVICE_WORKER: false, // After setting this false, recompile, then in Chrome DevTools go to Application Tab, Application/Service Worker and for the sw.js click the "unregister" button to the right + + DEBUG_MODE: false, + SHOW_TEST_OPTIONS: false, // On the DeviceDialog + + LOG_RENDER_EVENTS: false, + LOG_ONLY_FIRST_RENDER_EVENTS: false, + LOG_HTTP_REQUESTS: false, + LOG_ROUTING: false, + LOG_SIGNIN_STEPS: false, + LOG_CORDOVA_OFFSETS: false, + SHOW_CORDOVA_URL_FIELD: false, // Only needed for debugging in Cordova + + // Use 1 or 0 as opposed to true or false + test: { + use_test_election: 0, + }, + + location: { + text_for_map_search: '', + }, + + ENABLE_FACEBOOK: false, + ENABLE_TWITTER: true, + ENABLE_PAY_TO_PROMOTE: false, + + // API Keys, some of these are publishable (not secret) + FACEBOOK_APP_ID: "1097389196952441", + FULL_STORY_ORG: '', + GOOGLE_ADS_TRACKING_ID: 'T', + GOOGLE_ANALYTICS_TRACKING_ID: '', + GOOGLE_MAPS_API_KEY: '', + GOOGLE_PEOPLE_API_KEY: '', + GOOGLE_PEOPLE_API_CLIENT_ID: '', + GOOGLE_RECAPTCHA_KEY: '', + OPEN_REPLAY_PROJECT_KEY: '', + OPEN_REPLAY_INGEST_POINT: 'https://openreplay.wevote.us/ingest', + STRIPE_API_KEY: "", +}; diff --git a/src/js/pages/Process/TwitterSignInProcess.jsx b/src/js/pages/Process/TwitterSignInProcess.jsx index 63439598d..77d53ff3f 100644 --- a/src/js/pages/Process/TwitterSignInProcess.jsx +++ b/src/js/pages/Process/TwitterSignInProcess.jsx @@ -26,15 +26,30 @@ export default class TwitterSignInProcess extends Component { savingAccount: false, redirectInProgress: false, twitterAuthResponse: {}, + redirectCount: 0, }; + this.twitterOauthLeg3 = this.twitterOauthLeg3.bind(this); } componentDidMount () { + // console.log('--------------- TwitterSignInProcess componentDidMount'); this.appStateSubscription = messageService.getMessage().subscribe(() => this.onAppObservableStoreChange()); this.twitterStoreListener = TwitterStore.addListener(this.onTwitterStoreChange.bind(this)); this.voterStoreListener = VoterStore.addListener(this.onVoterStoreChange.bind(this)); - this.twitterSignInRetrieve(); window.scrollTo(0, 0); + this.twitterSignInRetrieve(); + const { location: { search } } = this.props; + const { redirectCount } = this.state; + oAuthLog('TwitterSignInProcess search props: ', search); + const urlParams = new URLSearchParams(search); + const oauthToken = urlParams.get('oauth_token'); + const oauthVerifier = urlParams.get('oauth_verifier'); + if (oauthToken && oauthVerifier && redirectCount === 0) { + oAuthLog('TwitterSignInProcess received redirect from Twitter redirectCount: ', redirectCount, ', oauthToken: ', oauthToken, ', oauthVerifier: ', oauthVerifier); + this.setState({ redirectCount: (redirectCount + 1) }); + TwitterActions.twitterOauth1UserHandler(oauthToken, oauthVerifier); + this.twitterSignInRetrieve(); + } } componentWillUnmount () { @@ -54,7 +69,10 @@ export default class TwitterSignInProcess extends Component { const { mergingTwoAccounts, savingAccount } = this.state; console.log('TwitterSignInProcess onTwitterStoreChange, twitterAuthResponse:', twitterAuthResponse); - if (twitterAuthResponse.twitter_sign_in_failed) { + if (twitterAuthResponse.twitter_sign_in_failed === undefined && twitterAuthResponse.twitter_oauth_voter_info_stored_in_db) { + oAuthLog('Twitter sign undefined, but oauth_voter_info_stored_in_db - calling twitterSignInRetrieve()'); + this.twitterSignInRetrieve(); + } else if (twitterAuthResponse.twitter_sign_in_failed) { oAuthLog('Twitter sign in failed - push to /settings/account'); historyPush({ pathname: '/settings/account', // SnackNotifier that handles this is in SettingsDashboard @@ -191,9 +209,17 @@ export default class TwitterSignInProcess extends Component { } twitterSignInRetrieve () { + oAuthLog('Twitter twitterSignInRetrieve on TwitterSignInProcess component mount'); TwitterActions.twitterSignInRetrieve(); } + twitterOauthLeg3 (oauthToken, oauthVerifier) { + const { redirectCount } = this.state; + oAuthLog('TwitterSignInProcess received redirect from Twitter redirectCount: ', redirectCount, ', oauthToken: ', oauthToken, ', oauthVerifier: ', oauthVerifier); + this.setState({ redirectCount: (redirectCount + 1) }); + TwitterActions.twitterOauth1UserHandler(oauthToken, oauthVerifier); + } + render () { renderLog('TwitterSignInProcess'); // Set LOG_RENDER_EVENTS to log all renders const { redirectInProgress, twitterAuthResponse } = this.state; diff --git a/src/js/stores/BallotStore.js b/src/js/stores/BallotStore.js index fb106099d..43dc17f78 100644 --- a/src/js/stores/BallotStore.js +++ b/src/js/stores/BallotStore.js @@ -879,7 +879,6 @@ class BallotStore extends ReduceStore { BallotActions.voterBallotItemsRetrieve(); return this.resetState(); - case 'twitterNativeSignInSave': case 'twitterSignInRetrieve': case 'voterEmailAddressSignIn': case 'voterFacebookSignInRetrieve': diff --git a/src/js/stores/FriendStore.js b/src/js/stores/FriendStore.js index 7bdbec4d7..685a55db1 100644 --- a/src/js/stores/FriendStore.js +++ b/src/js/stores/FriendStore.js @@ -439,7 +439,6 @@ class FriendStore extends ReduceStore { currentFriendsOrganizationWeVoteIds, }; - case 'twitterNativeSignInSave': case 'twitterSignInRetrieve': case 'voterEmailAddressSignIn': case 'voterFacebookSignInRetrieve': diff --git a/src/js/stores/IssueStore.js b/src/js/stores/IssueStore.js index fe34cfc91..d92b81d7e 100644 --- a/src/js/stores/IssueStore.js +++ b/src/js/stores/IssueStore.js @@ -966,7 +966,6 @@ class IssueStore extends ReduceStore { issueWeVoteIdsUnderEachBallotItem, organizationWeVoteIdsLinkedToIssueDict, }; - case 'twitterNativeSignInSave': case 'twitterSignInRetrieve': case 'voterEmailAddressSignIn': case 'voterFacebookSignInRetrieve': diff --git a/src/js/stores/OrganizationStore.js b/src/js/stores/OrganizationStore.js index 5ce96edd8..1a72ced23 100644 --- a/src/js/stores/OrganizationStore.js +++ b/src/js/stores/OrganizationStore.js @@ -1006,7 +1006,6 @@ class OrganizationStore extends ReduceStore { organizationWeVoteIdsVoterIsIgnoring, }; - case 'twitterNativeSignInSave': case 'twitterSignInRetrieve': case 'voterEmailAddressSignIn': case 'voterFacebookSignInRetrieve': diff --git a/src/js/stores/SupportStore.js b/src/js/stores/SupportStore.js index a9a705d07..c045f8231 100644 --- a/src/js/stores/SupportStore.js +++ b/src/js/stores/SupportStore.js @@ -234,7 +234,6 @@ class SupportStore extends ReduceStore { SupportActions.voterAllPositionsRetrieve(); return this.resetState(); - case 'twitterNativeSignInSave': case 'twitterSignInRetrieve': case 'voterEmailAddressSignIn': case 'voterFacebookSignInRetrieve': diff --git a/src/js/stores/TwitterStore.js b/src/js/stores/TwitterStore.js index 6feefb619..933c5e2be 100644 --- a/src/js/stores/TwitterStore.js +++ b/src/js/stores/TwitterStore.js @@ -1,15 +1,15 @@ import { ReduceStore } from 'flux/utils'; import CandidateActions from '../actions/CandidateActions'; import OrganizationActions from '../actions/OrganizationActions'; -import TwitterActions from '../actions/TwitterActions'; import VoterActions from '../actions/VoterActions'; import Dispatcher from '../common/dispatcher/Dispatcher'; class TwitterStore extends ReduceStore { getInitialState () { - // return { - // success: true, - // }; + return { + twitter_store_initialized: true, + twitter_oauth_voter_info_stored_in_db: false, + }; } get () { @@ -101,6 +101,7 @@ class TwitterStore extends ReduceStore { existing_twitter_account_found: this.getState().existing_twitter_account_found, voter_we_vote_id_attached_to_twitter: this.getState().voter_we_vote_id_attached_to_twitter, twitter_image_load_info: this.getState().twitter_image_load_info, + twitter_oauth_voter_info_stored_in_db: this.getState().twitter_oauth_voter_info_stored_in_db, }; } @@ -156,21 +157,6 @@ class TwitterStore extends ReduceStore { status: action.res.status, }; - case 'twitterNativeSignInSave': - // Exit if we don't have a successful response (since we expect certain variables in a successful response below) - if (!action.res || !action.res.success) return state; - if (action.res.success) { - TwitterActions.twitterSignInRetrieve(); - } - - return { - // ...state, - voter_device_id: action.res.voter_device_id, - twitter_handle: action.res.twitter_handle, - twitter_handle_found: action.res.twitter_handle_found, - twitter_secret_key: action.res.twitter_secret_key, - }; - case 'twitterProcessDeferredImages': if (!action.res || !action.res.success) return state; // console.log('twitter twitterProcessDeferredImages', action.res); @@ -183,10 +169,12 @@ class TwitterStore extends ReduceStore { we_vote_hosted_profile_image_url_tiny: action.res.we_vote_hosted_profile_image_url_tiny, }; - case 'twitterSignInRetrieve': - // Exit if we don't have a successful response (since we expect certain variables in a successful response below) - if (!action.res || !action.res.success) return state; + // console.log('twitterSignInRetrieve in TwitterStore received: ', action.res); + if (!action.res || !action.res.success) { + // Exit if we don't have a successful response (since we expect certain variables in a successful response below) + return state; + } if (action.res.twitter_sign_in_verified) { VoterActions.voterRetrieve(); VoterActions.twitterRetrieveIdsIfollow(); @@ -217,10 +205,16 @@ class TwitterStore extends ReduceStore { // return this.resetState(); return this.resetVoterSpecificData(); - default: + case 'twitterOauth1UserHandler': + if (!action.res || !action.res.success) return state; + console.log('twitterOauth1UserHandler res: ', action.res); return { ...state, + twitter_oauth_voter_info_stored_in_db: true, }; + + default: + return state; } } } diff --git a/src/js/stores/VoterGuideStore.js b/src/js/stores/VoterGuideStore.js index 9a054d4b8..26ed83aff 100644 --- a/src/js/stores/VoterGuideStore.js +++ b/src/js/stores/VoterGuideStore.js @@ -868,7 +868,6 @@ class VoterGuideStore extends ReduceStore { organizationWeVoteIdsToFollowForLatestBallotItem: state.organizationWeVoteIdsToFollowForLatestBallotItem.filter((existingOrgWeVoteId) => existingOrgWeVoteId !== organizationWeVoteId), }; - case 'twitterNativeSignInSave': case 'twitterSignInRetrieve': case 'voterEmailAddressSignIn': case 'voterFacebookSignInRetrieve':