Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Popup stays open after authentication succeeds #21

Open
contomcang opened this issue May 3, 2018 · 4 comments
Open

Popup stays open after authentication succeeds #21

contomcang opened this issue May 3, 2018 · 4 comments

Comments

@contomcang
Copy link

I got this problem trying to implement Twitter login button in reactjs.
After clicking on the login button and logging in successfully the popup window was not closed. I took a close look at react-twitter-auth-component.js and found out popup.location was blank (line 413). Is it just me or anyone else has encountered the same error?

@funador
Copy link

funador commented May 21, 2018

Reading around it looks like you can't access the popup the way this component was built. Only way I could think of to get around that was to use a socket to kick back the info to the client once you have authenticated successfully:

// Client
import React, { Component } from 'react'
import openSocket from 'socket.io-client'
import uuidv4 from 'uuid/v4'

const API_URL = 'http://127.0.0.1:8080'
const socket = openSocket(API_URL)

class TwitterLogin extends Component {
  
  constructor(props) {
    super(props)
    this.state = {
      socket: uuidv4(),
      user: 'nothing'
    }  
  }

  componentDidMount() {
    this.subscribeToAuth()
  }

  subscribeToAuth() {
    socket.on('connect', () => {
       socket.emit('room', this.state.socket)
    })

    socket.on('user', user => {
      this.setState({user})
      this.popup.close()
    })
  }

  openPopup() {
    const width = 600
    const height = 400
    const left = (window.innerWidth / 2) - (width / 2)
    const top = (window.innerHeight / 2) - (height / 2)

    return window.open(
      '', '', 
      `toolbar=no, location=no, directories=no, status=no, menubar=no, 
      scrollbars=no, resizable=no, copyhistory=no, width=${width}, 
      height=${height}, top=${top}, left=${left}`
    )
  }

  authUser() {
    const url = `${API_URL}/twitter?socket=${this.state.socket}`
    this.popup = this.openPopup()
    this.popup.location.replace(url)
  }

  onButtonClick(e) {
    e.preventDefault()
    this.authUser()
  }

  render() {
    return (
      <div>
        <button onClick={this.onButtonClick.bind(this)}>
          Twitter
        </button>
        <div>
          {this.state.user}
        </div>
      </div>
    )
  }
}

export default TwitterLogin
// Server
const express = require('express')
const passport = require('passport')
const { Strategy: TwitterStrategy } = require('passport-twitter')
const cors = require('cors')
const session = require('express-session')

const app = express()
const server = require('http').createServer(app)
const io = require('socket.io')(server)

passport.use(new TwitterStrategy({
    consumerKey: 'your key',
    consumerSecret: 'your secret',
    // have to set this callback url on apps.twitter.com
    callbackURL: 'http://127.0.0.1:8080/twitter/callback',
  },
  (req, token, tokenSecret, profile, cb) => cb(null, profile)
))

passport.serializeUser((user, cb) => cb(null, user))

passport.deserializeUser((obj, cb) => cb(null, obj))

app.use(cors({
  origin: 'http://localhost:3000'
})) 

app.use(session({ 
  secret: 'KeyboardKitty', 
  resave: true, 
  saveUninitialized: true 
}))

app.use(express.json())
app.use(passport.initialize())
app.use(passport.session())

app.set('socketio', io)
io.sockets.on('connection', socket => {
  socket.on('room', room => {     
    socket.join(room)
  })
})

const addSocketToSession = (req, res, next) => {
  req.session.socket = req.query.socket
  next()
}

const twitterAuth = passport.authenticate('twitter')

app.get('/twitter', addSocketToSession, twitterAuth)

app.get('/twitter/callback', twitterAuth, (req, res) => {
  const io = req.app.get('socketio')
  io.sockets.in(req.session.socket).emit('user', req.user.username)
  res.send('all done!')
})

server.listen(8080)

@ivanvs
Copy link
Contributor

ivanvs commented May 21, 2018

Well in this example you are using wrong Node.js library. In tutorial and in example I have used passport-twitter-token and in this example you are using passport-twitter. That is big difference. If you are using that library, then yes, you would need web sockets or something similar.

@funador
Copy link

funador commented May 21, 2018

Its also possible to use what I wrote with passport-twitter-token and without the sockets. You just send the callback to localhost:3000 like you did in your example. It feels convoluted bouncing back and forth to the server like that and writing a bunch of requests on the server that passport is set up to do in the first place. Sockets is just a different approach, I guess.

@jonesty
Copy link

jonesty commented May 21, 2019

Hi from the future. I just had the same issue. Not sure if this helps but make sure your callback URL has the same origin as your app that opens the popup window, otherwise it won't be able to access the popup's properties and the popup will stay open. https://developer.mozilla.org/en-US/docs/Web/API/Window/open#Return_value

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants