question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

404 sent to client (browser) during google callback, but the user data is retrieved.

See original GitHub issue

When calling passport.authenticate(...) in the OAuth2 callback stage of the authentication sequence for Google OAuth2 as follows:

const GoogleStrategy = require('passport-google-oauth2').Strategy
passport.use(new GoogleStrategy({
    scope: ['email', 'profile'],
    clientID: process.env.GOOGLE_CLIENT_ID,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    callbackURL: 'http://localhost:' + 7000 + '/auth/google/callback'
  },
  (accessToken, refreshToken, profile, done) => {
    console.log(profile)
    fetchUser().then(user => done(null, user))
  }
))


app.use(route.get('/auth/google', async (ctx, next) => {
  console.log(`/api/google/singin called with\n ctx -> ${JSON.stringify(ctx)} && next -> ${next}`)
    console.log(`ctx.req.headers -> ${JSON.stringify(ctx.req.headers)}`)
    console.log(`ctx.req.rawHeaders -> ${ctx.req.rawHeaders}`)
    passport.authenticate('google')(ctx,next)
  }
))
  
app.use(route.get('/auth/google/callback', async (ctx, next) => {
  passport.authenticate('google', async (err, user, info) => {
    console.log(`/auth/google/signin/callback -> passport.authenticate('google') callback called with\nerr -> ${JSON.stringify(err)}\nuser -> ${JSON.stringify(user)}\ninfo -> ${JSON.stringify(info)}`)
    if (user === false) {
      ctx.redirect('/')
      await next()
    } else {
      ctx.login(user)
      ctx.redirect('/app')
      await next()
    }
  })(ctx, next)
}))

Koa is sending 404 headers to the client (browser),

screen shot 2017-04-17 at 6 17 08 pm

but the anonymous function callback is called with the actual user data.

The debug output shows the sequence of calls (removed personal info):

  <-- GET /favicon.ico
  --> GET /favicon.ico 302 6ms 33b
  <-- GET /
  --> GET / 200 5ms 719b
  <-- GET /auth/google
/api/google/singin called with
 ctx -> {"request":{"method":"GET","url":"/auth/google","header":{"host":"localhost:7000","connection":"keep-alive","upgrade-insecure-requests":"1","user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36","accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8","dnt":"1","referer":"http://localhost:7000/","accept-encoding":"gzip, deflate, sdch, br","accept-language":"en-US,en;q=0.8,ja;q=0.6","cookie":"koa.sid.sig=O9oTTBivYar3gy7JuZtdpDgvTO0"}},"response":{"status":404,"message":"Not Found","header":{}},"app":{"subdomainOffset":2,"proxy":true,"env":"development"},"originalUrl":"/auth/google","req":"<original node req>","res":"<original node res>","socket":"<original node socket>"} && next -> function next() {
          return dispatch(i + 1)
        }
ctx.req.headers -> {"host":"localhost:7000","connection":"keep-alive","upgrade-insecure-requests":"1","user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36","accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8","dnt":"1","referer":"http://localhost:7000/","accept-encoding":"gzip, deflate, sdch, br","accept-language":"en-US,en;q=0.8,ja;q=0.6","cookie":"koa.sid.sig=O9oTTBivYar3gy7JuZtdpDgvTO0"}
ctx.req.rawHeaders -> Host,localhost:7000,Connection,keep-alive,Upgrade-Insecure-Requests,1,User-Agent,Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36,Accept,text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8,DNT,1,Referer,http://localhost:7000/,Accept-Encoding,gzip, deflate, sdch, br,Accept-Language,en-US,en;q=0.8,ja;q=0.6,Cookie,koa.sid.sig=O9oTTBivYar3gy7JuZtdpDgvTO0
  --> GET /auth/google 302 4ms 0b
  <-- GET /auth/google/callback?code=4/vE4A-Yl3nZS07-x61cgapHEf2UhX3b-ptn4K0wdSLv0
  --> GET /auth/google/callback?code=4/vE4A-Yl3nZS07-x61cgapHEf2UhX3b-ptn4K0wdSLv0 404 9ms -
  <-- GET /favicon.ico
  --> GET /favicon.ico 302 2ms 33b
  <-- GET /
  --> GET / 200 22ms 719b
{ provider: 'google',
  id: 'XXXXXXXXXXXXXXX',
  displayName: XXXXXXXXXXXXXXX',
  name: { familyName: 'XXXXXXXXXXXXXXX', givenName: 'XXXXXXXXXXXXXXX' },
  isPerson: true,
  isPlusUser: true,
  language: 'en',
  emails: [ { value: 'XXXXXXXXXXXXXXX', type: 'account' } ],
  email: 'XXXXXXXXXXXXXXX',
  gender: 'XXXXXXXXXXXXXXX',
  photos: [ { value: 'https://lh5.googleusercontent.com/XXXXXXXXXXX/photo.jpg?sz=50' } ],
  _raw: '{\n "kind": "plus#person",\n "etag": "\\"XXXXXXXXXXXXXX\\"",\n "gender": "XXXXXXXXX",\n "emails": [\n  {\n   "value": "XXXXXXXXXXXXXXX",\n   "type": "account"\n  }\n ],\n "objectType": "person",\n "id": "XXXXXXXXXXXXXXX",\n "displayName": "XXXXXXXXXXXXXXX",\n "name": {\n  "familyName": "XXXXXXXXXXXXXXX",\n  "givenName": "XXXXXXXXXXXXXXX"\n },\n "url": "https://plus.google.com/XXXXXXXXXXXXXXX",\n "image": {\n  "url": "https://lh5.googleusercontent.com/XXXXXXXXXXXXXXX/photo.jpg?sz=50",\n  "isDefault": false\n },\n "isPlusUser": true,\n "language": "en",\n "circledByCount": 0,\n "verified": false\n}\n',
  _json: 
   { kind: 'plus#person',
     etag: '"XXXXXXXXXXXXXXX"',
     gender: 'XXXXXXXXXXXXXXX',
     emails: [ [Object] ],
     objectType: 'person',
     id: 'XXXXXXXXXXXXXXX',
     displayName: 'XXXXXXXXXXXXXXX',
     name: { familyName: 'XXXXXXXXXXXXXXX', givenName: 'XXXXXXXXXXXXXXX' },
     url: 'https://plus.google.com/XXXXXXXXXXXXXXX',
     image: 
      { url: 'https://lh5.googleusercontent.com/XXXXXXXXXXXXXXX/photo.jpg?sz=50',
        isDefault: false },
     isPlusUser: true,
     language: 'en',
     circledByCount: 0,
     verified: false } }
/auth/google/signin/callback -> passport.authenticate('google') callback called with
err -> null
user -> {"id":1,"username":"test","password":"test"}
info -> {}
(node:54360) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 4): Error: Can't set headers after they are sent.

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:7 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
Panoploscommented, Apr 18, 2017

And we have liftoff!!

For documentation purposes, here is the code that works:

app.use(route.get('/auth/google/callback', async (ctx, next) => {
  await passport.authenticate('google', async (err, user, info) => {
    if (user === false) {
      ctx.redirect('/')
    } else {
      await ctx.login(user)
      ctx.redirect('/app')
    }
  })(ctx)
}))
1reaction
Panoploscommented, Aug 8, 2017

It is sent to the function returned by passport.authenticate(...).

Read more comments on GitHub >

github_iconTop Results From Across the Web

404 sent to client (browser) during google callback, but the ...
404 sent to client (browser) during google callback, but the user data is retrieved. #25. Panoplos opened this issue on Apr 17, 2017...
Read more >
Google OAuth works fine in localhost but not on Heroku, it ...
I've implemented Google OAuth sign in in my PERN stack application successfully in localhost, now deploying it to heroku almost all works ...
Read more >
404. That's an error. The requested URL was not found on this ...
As you might know, a 404 error is "File not found." This can happen when a file is deleted or there is an...
Read more >
Using OAuth 2.0 for Web Server Applications | Authorization
This document explains how web server applications use Google API Client Libraries or Google OAuth 2.0 endpoints to implement OAuth 2.0 ...
Read more >
How the fix redirect_uri_mismatch error. Part 2 server sided ...
The redirect_uri_mismatch error message is quite common when working with the Google APIs. Google APIs support authorization to private user ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found