fastify-passport calls local route instead of Google /o/oauth2/v2/auth
clubside opened this issue · 1 comments
💬 Question here
Howdy! I had everything working great in my localhost setup. Unfortunately when I moved to testing using a public-facing URL attempting to login went to my site rather than Google's causing a 404. I am hosting this dev site as https://swift.saisquared.dev/ which uses IIS to reverse proxy to the original localhost environment http://localhost:3940. Because of this setup you can view the site on the server using either address, but only the latter actually lets you log in.
const app = fastify({
logger: {
level: 'debug',
transport: {
target: '@mgcrea/pino-pretty-compact',
options: { translateTime: 'SYS:mm/dd/yy HH:MM:ss', ignore: 'pid,hostname' }
}
},
disableRequestLogging: true
})
app.register(async function (app) {
app.register(fastifyRequestLogger)
app.register(fastifySecureSession, {
key: fs.readFileSync(path.join(__dirname, 'secret-key')),
cookie: {
path: '/'
}
})
app.register(fastifyPassport.initialize())
app.register(fastifyPassport.secureSession())
app.register(fastifyFormBody, {
bodyLimit: 104857600000000
})
fastifyPassport.registerUserSerializer(
async (user, request) => {
console.log('registerUserSerializer', { user }, { json: user._json })
const userJson = user._json
const { id, displayName } = user
/** @type {LoginWeb} */
const loginData = {
email: userJson.email,
name: userJson.name,
avatar: userJson.picture
}
/** @type {LoginWebResult} */
const loginWebResult = await loginWeb(loginData)
// User object sent it from Google.
const userForSession = { id, displayName, email: userJson.email, role: loginWebResult.userRights.role }
return userForSession
}
)
fastifyPassport.registerUserDeserializer(async (userFromSession, request) => {
console.log('registerUserDeserializer', { userFromSession })
return userFromSession
})
fastifyPassport.use('google', new GoogleStrategy.OAuth2Strategy({
clientID: process.env.AUTH0_CLIENT_ID,
clientSecret: process.env.AUTH0_CLIENT_SECRET,
callbackURL: 'https://lswift.saisquared.dev/api/auth/google/callback'
}, function (accessToken, refreshToken, profile, cb) {
// console.log({ accessToken, refreshToken, profile })
return cb(null, profile)
}
))
app.setNotFoundHandler(async (request, reply) => {
// console.log({ setNotFoundHandler: request })
returnNotFound(request, reply)
})
app.get('/login',
{
preValidation: fastifyPassport.authenticate('google', { scope: ['profile', 'email'] })
},
async () => {
console.log('GOOGLE API forward')
}
)
app.get('/api/auth/google/callback',
{
preValidation: fastifyPassport.authenticate('google', { scope: ['profile', 'email'] })
},
function (request, reply) {
reply.redirect('/profile')
}
)
app.get('/logout', (request, reply) => {
request.session.delete()
reply.redirect('/')
})
await adminPages(app)
await profilePages(app)
await standardPages(app)
await clientAPIs(app)
})
// static file server
app.register(fastifyStatic, {
root: path.join(__dirname, 'public')
})
app.listen({ port: 3940 }, (err, address) => {
if (err) throw err
})
If you were to go to the live site now https://swift.saisquared.dev/ and click the Login button you would get my 404 page. In the console you would see Fastify's logger display:
07/13/24 00:14:18 info: #req-s ←GET:/login request from ip ::1 (fastify-request-logger)
07/13/24 00:14:18 info: #req-s →GET:/login response with a 302-status took 4.166ms (fastify-request-logger)
07/13/24 00:14:18 warn: #req-t →GET:/o/oauth2/v2/auth?response_type=code&redirect_uri=https%3A%2F%2Fswift.saisquared.dev%2Fapi%2Fauth%2Fgoogle%2Fcallback&scope=profile%20email&client_id=294398145538-hm97gsnslbpdi47or8csqlaoic51dcrt.apps.googleusercontent.com response with a 404-status took 16.287ms (fastify-request-logger)
With this debug information:
{
setNotFoundHandler: Request {
id: 'req-t',
params: { '*': 'o/oauth2/v2/auth' },
raw: IncomingMessage {
_events: [Object],
_readableState: [ReadableState],
_maxListeners: undefined,
socket: [Socket],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
rawHeaders: [Array],
rawTrailers: [],
joinDuplicateHeaders: null,
aborted: false,
upgrade: false,
url: '/o/oauth2/v2/auth?response_type=code&redirect_uri=https%3A%2F%2Fswift.saisquared.dev%2Fapi%2Fauth%2Fgoogle%2Fcallback&scope=profile%20email&client_id=294398145538-hm97gsnslbpdi47or8csqlaoic51dcrt.apps.googleusercontent.com',
method: 'GET',
statusCode: null,
statusMessage: null,
client: [Socket],
_consuming: false,
_dumped: false,
[Symbol(shapeMode)]: true,
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: [Object],
[Symbol(kHeadersCount)]: 42,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0
},
query: Empty <[Object: null prototype] {}> {
response_type: 'code',
redirect_uri: 'https://swift.saisquared.dev/api/auth/google/callback',
scope: 'profile email',
client_id: '294398145538-hm97gsnslbpdi47or8csqlaoic51dcrt.apps.googleusercontent.com'
},
log: EventEmitter {
trace: [Function: noop],
debug: [Function: LOG],
info: [Function: LOG],
warn: [Function: LOG],
error: [Function: LOG],
fatal: [Function (anonymous)],
[Symbol(pino.serializers)]: [Object],
[Symbol(pino.formatters)]: [Object],
[Symbol(pino.chindings)]: ',"pid":12824,"hostname":"CLUBDEVSRV","reqId":"req-t"',
[Symbol(pino.levelVal)]: 20,
[Symbol(fastify.disableRequestLogging)]: true
},
body: undefined,
[Symbol(fastify.context)]: {
schema: undefined,
handler: [Function: bound ] AsyncFunction,
Reply: [Function],
Request: [Function],
contentTypeParser: [ContentTypeParser],
onRequest: [Array],
onSend: null,
onError: null,
onTimeout: null,
preHandler: [Array],
onResponse: [Array],
preSerialization: null,
onRequestAbort: null,
config: {},
errorHandler: [Object],
requestIdLogLabel: 'reqId',
childLoggerFactory: [Function: defaultChildLoggerFactory],
_middie: null,
_parserOptions: [Object],
exposeHeadRoute: undefined,
prefixTrailingSlash: undefined,
logLevel: '',
logSerializers: undefined,
attachValidation: undefined,
schemaErrorFormatter: [Function: defaultSchemaErrorFormatter],
validatorCompiler: null,
serializerCompiler: null,
server: [Object],
preParsing: null,
preValidation: [Array],
[Symbol(fastify.404ContextKey)]: null,
[Symbol(fastify.replySerializerDefault)]: undefined,
[Symbol(fastify.routeByFastify)]: undefined,
[Symbol(fastify.request.cache.validateFns)]: null,
[Symbol(fastify.reply.cache.serializeFns)]: null,
[Symbol(fastify.routeOptions)]: [Object: null prototype]
},
[Symbol(fastify.RequestPayloadStream)]: IncomingMessage {
_events: [Object],
_readableState: [ReadableState],
_maxListeners: undefined,
socket: [Socket],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
rawHeaders: [Array],
rawTrailers: [],
joinDuplicateHeaders: null,
aborted: false,
upgrade: false,
url: '/o/oauth2/v2/auth?response_type=code&redirect_uri=https%3A%2F%2Fswift.saisquared.dev%2Fapi%2Fauth%2Fgoogle%2Fcallback&scope=profile%20email&client_id=294398145538-hm97gsnslbpdi47or8csqlaoic51dcrt.apps.googleusercontent.com',
method: 'GET',
statusCode: null,
statusMessage: null,
client: [Socket],
_consuming: false,
_dumped: false,
[Symbol(shapeMode)]: true,
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: [Object],
[Symbol(kHeadersCount)]: 42,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0
}
}
}
However if I'm on that server and go to http://localhost:3940 (which serves up the identical site) and click the Login button it goes ahead to Google to choose an account and the Fastify log shows:
07/13/24 00:14:46 info: #req-13 ←GET:/login request from ip ::1 (fastify-request-logger)
07/13/24 00:14:46 info: #req-13 →GET:/login response with a 302-status took 1.635ms (fastify-request-logger)
So you can see when someone is at https://swift.saisquared.dev/ and Login is clicked the URL changes to
https://swift.saisquared.dev/o/oauth2/v2/auth?response_type=code&redirect_uri=https%3A%2F%2Fswift.saisquared.dev%2Fapi%2Fauth%2Fgoogle%2Fcallback&scope=profile%20email&client_id=294398145538-hm97gsnslbpdi47or8csqlaoic51dcrt.apps.googleusercontent.com
rather than the URL http://localhost:3940 changes to
https://accounts.google.com/o/oauth2/v2/auth/oauthchooseaccount?response_type=code&redirect_uri=https%3A%2F%2Fswift.saisquared.dev%2Fapi%2Fauth%2Fgoogle%2Fcallback&scope=profile%20email&client_id=294398145538-hm97gsnslbpdi47or8csqlaoic51dcrt.apps.googleusercontent.com&service=lso&o2v=2&ddm=0&flowName=GeneralOAuthFlow
Somehow, be it the reverse proxy or some other thing happening in the middle Passport is choosing to call my server for the Google login page rather than Google as it should.
I did a lot of searching and could not find a similar problem but given all the variables it seems like there would be a simple solution. Would really appreciate some help as the next step is to deploy to the real website.
P.S. I went so far as trying to do this with @fastify/oauth2
before asking for help and it produces the same result, everything works access the site using localhost but not with swift.saisquared.dev.
Your Environment
- node version: 22.3.0
- fastify version: 4.28.1
- @fastify/passport version: 2.5.0
- os: Windows Server 2019
- web server: IIS 10
Sorry for the trouble, it was my fault related to outbound redirects from IIS, just needed to change one option on the server and everything is working.