I want to construct FirebaseAuthentication with Twitter using Expo.But an error that the request token is invalid occurred.
Running server side program with my PC and Expo(client) side program with Expo app in my iPhone.
(* is each token and so on)
Server side (JavaScript)
const bodyParser = require('body-parser');const fetch = require('node-fetch');const OAuth = require('oauth-1.0a');const qs = require('qs')const HmacSHA1 = require('crypto-js/hmac-sha1')const Base64 = require('crypto-js/enc-base64')const port = process.env.PORT || 3000const config = { GOOGLE: { CLIENT_ID: "", CLIENT_SECRET: "", }, GITHUB: { CLIENT_ID: "", CLIENT_SECRET: "", }, TWITTER: { CLIENT_ID: "*****", CLIENT_SECRET: "*****", }}const app = express();app.use(bodyParser.urlencoded({ extended: true }));app.use(bodyParser.json());app.post('/auth/google', async (req, res) => { async function createTokenWithGoogleCode(code, redirect_uri) { const url = `https://www.googleapis.com/oauth2/v4/token` const res = await fetch(url, { method: 'POST', body: JSON.stringify({ code, client_id: config.GOOGLE.CLIENT_ID, client_secret: config.GOOGLE.CLIENT_SECRET, redirect_uri, grant_type: 'authorization_code' }) }); return await res.json() } return res.json(await createTokenWithGoogleCode(req.body.code, req.body.redirect_uri))});app.post('/auth/github', async (req, res) => { async function createTokenWithGithubCode(code) { const url = `https://github.com/login/oauth/access_token` + `?client_id=${config.GITHUB.CLIENT_ID}` + `&client_secret=${config.GITHUB.CLIENT_SECRET}` + `&code=${code}`; const res = await fetch(url, { method: 'POST', headers: { Accept: 'application/json','Content-Type': 'application/json', }, }); return await res.json() } return res.json(await createTokenWithGithubCode(req.body.code))});app.post('/auth/twitter/request_token', async (req, res) => { const { redirect_uri } = req.body const oauth = OAuth({ consumer: { key: config.TWITTER.CLIENT_ID, secret: config.TWITTER.CLIENT_SECRET, }, signature_method: 'HMAC-SHA1', hash_function: (baseString, key) => Base64.stringify(HmacSHA1(baseString, key)) }) const request_data = { url: 'https://api.twitter.com/oauth/request_token', method: 'POST', data: { oauth_callback: redirect_uri, } }; const response = await fetch(request_data.url, { method: request_data.method, headers: oauth.toHeader(oauth.authorize(request_data)) }) const text = await response.text(); return res.json(qs.parse(text))});app.post('/auth/twitter/access_token', async (req, res) => { const { oauth_token, oauth_token_secret, oauth_verifier } = req.body const oauth = OAuth({ consumer: { key: config.TWITTER.CLIENT_ID, secret: config.TWITTER.CLIENT_SECRET, }, signature_method: 'HMAC-SHA1', hash_function: (baseString, key) => Base64.stringify(HmacSHA1(baseString, key)) }) const request_data = { url: 'https://api.twitter.com/oauth/access_token', method: 'POST', data: { oauth_verifier, }, } const headers = oauth.toHeader(oauth.authorize(request_data, {key: oauth_token, secret: oauth_token_secret})) const response = await fetch(request_data.url, { method: request_data.method, data: request_data.data, headers }) if (response.status !== 200) { res.status = response.status return res.json({message: "something wrong"}) } const text = await response.text(); return res.json(qs.parse(text))})if (!module.parent) { app.listen(3000, () => { console.log('Example app listening on port 3000!'); });}module.exports = app;Expoexpo init expo && cd $_yarn add firebase
Expo side (TypeScript)
import React from 'react'import { StyleSheet, Text, Button, View } from 'react-native'import { Facebook } from 'expo';import * as AuthSession from "expo-auth-session"import firebase from 'firebase';const AUTH_BASE_URL = "http://localhost:3000"const REDIRECT_URL = AuthSession.getRedirectUrl();const FACEBOOK_APP_ID = ""const GOOGLE_CLIENT_ID = ""const GITHUB_CLIENT_ID = ""if (!firebase.apps.length) { firebase.initializeApp({ apiKey: "*****", authDomain: "*****", databaseURL: "*****", projectId: "*****", storageBucket: "*****", messagingSenderId: "*****" })}async function createTokenWithCode(provider, code) { const url = `${AUTH_BASE_URL}/auth/${provider}/` console.log(url) const res = await fetch(url, { method: 'POST', headers: {'Accept': 'application/json','Content-Type': 'application/json', }, body: JSON.stringify({ code, redirect_uri: REDIRECT_URL // Google で必要 }) }) const json = await res.json(); console.log(json) return json}async function getTwitterRequestToken() { const url = `${AUTH_BASE_URL}/auth/twitter/request_token` const res = await fetch(url, { method: 'POST', headers: {'Accept': 'application/json','Content-Type': 'application/json', }, body: JSON.stringify({ redirect_uri: REDIRECT_URL }) }); return await res.json();}async function getTwitterAccessToken(params) { const { oauth_token, oauth_token_secret, oauth_verifier } = params const url = `${AUTH_BASE_URL}/auth/twitter/access_token` const res = await fetch(url, { method: 'POST', headers: {'Accept': 'application/json','Content-Type': 'application/json', }, body: JSON.stringify({ oauth_token, oauth_token_secret, oauth_verifier }) }); return await res.json();}export default class App extends React.Component { constructor(props) { super(props) this.state = { user: null } firebase.auth().onAuthStateChanged((user) => { if (user) { this.setState({ user: user.toJSON() }) console.log(user) } else { this.setState({ user: null }) } }) } handleLogout() { firebase.auth().signOut() } async handleTwitterLogin() { const { oauth_token, oauth_token_secret } = await getTwitterRequestToken() const { params } = await AuthSession.startAsync({ authUrl: `https://api.twitter.com/oauth/authenticate?oauth_token=${oauth_token}` }); const oauth_verifier = params.oauth_verifier const result = await getTwitterAccessToken({oauth_token, oauth_token_secret, oauth_verifier}) const credential = firebase.auth.TwitterAuthProvider.credential( result.oauth_token, result.oauth_token_secret ) firebase.auth().signInAndRetrieveDataWithCredential(credential); } render() { return (<View style={styles.container}><Text>Firebase Authentication Example</Text><Button onPress={this.handleTwitterLogin} title="Twitter" /><Button onPress={this.handleLogout} title="Logout" /><Text>displayName: {this.state.user && this.state.user.displayName}</Text><Text>providerId: {this.state.user && this.state.user.providerData[0].providerId}</Text></View> ); }}const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center', },});
I referred to following page.https://qiita.com/okamuuu/items/6da12f295c3b8a7bc3d8
Please tell me the solution.
I'm sorry if I can't make you understood.