I have two Node.js server apps. The first one runs Koa.JS on localhost:8081, while the second one runs Vue.JS on localhost:8080.In both servers, I am using HTTP not HTTPS.
The Koa.JS server uses Passport.JS to perform oAuth2 login flow, as well as providing endpoints to fetch data from an API and passing the bearer token to the Authorization header.
The Vue.js server is in charge of the client-side code. It calls the Koa endpoints using axios library.
If I open a browser, and test the login flow against the Koa server, all is well and working. Here are the steps:
localhost:8081/api/oauth/authenticate
router.get( '/authenticate', passport.authenticate( 'oauth2', { scope: config.scope } ))
- User logs in and grant access
When done, callback is called localhost:8081/api/oauth/callback
router.get( '/callback', ctx => { return passport.authenticate( 'oauth2', async (err, user) => { if (err) ctx.throw(err) const tokenSession = new token(ctx.session) await ctx.login(user) tokenSession.setPublicCredentials(user) ctx.redirect(`${config.vuehost}/auth?isUserLoggedIn=true`) })(ctx) })
- Session is saved with user info
User opens new tab to go to localhost:8081/api/user/profile
router.get( '/user/profile', async (ctx) => { if (ctx.isAuthenticated) { const options = { headers: { Authorization: `Bearer ${ctx.session.passport.user.access_token}` }, json: true, method: 'GET', uri: 'https://developer.mycoolapi.com/userprofile/v1/users/@me' } const response = await rp(options) ctx.body = JSON.stringify(response) } else { ctx.throw(401) } } )
- The Koa server calls the other API to retrieve user profile data, and Vue.js app gets a JSON response that is correct
However, if I do the following the ctx.session gets lost:
- Navigate to localhost:8080 (Vue.js server)
- Perform login by redirecting to Koa endpoint localhost:8081/api/oauth/authenticate
- Logging in and granting access
- On Koa /callback redirect back to localhost:8080/auth?isUserLoggedIn=true
In Vue app, retrieve query param using this.$route.query.isUserLoggedIn and if true, call the Koa endpoint to get user profile data localhost:8081/api/user/profile
axios.get('http://localhost:8081/api/user/profile') .then (response => { console.info(`\nsetUserData response: ${JSON.stringify(response)}\n`) }) .catch (err => { console.info(`\nsetUserData error: ${JSON.stringify(err)}\n`) })
This last step returns a 401 Unauthorized.
Upon further investigation the Koa route that has the profile endpoint is in the appRoutes middleware. This middleware is right after the app.use(requireLogin) middleware that checks if the session is authenticated (ctx.isAuthenticated()).
'use strict'
const requireLogin = async (ctx, next) => {
if (ctx.isAuthenticated()) {
await next()
} else {
ctx.status = 401
ctx.body = {
errors: [{ title: 'Login required', status: 401 }]
}
}
}
module.exports = requireLogin
This is where the 401 error occurs, as the ctx session is null at that point.
// Add routing
app.use(authRoutes.routes())
app.use(authRoutes.allowedMethods())
app.use(requireLogin)
app.use(appRoutes.routes())
app.use(appRoutes.allowedMethods())
Am I dealing with a timing issue of some sort? I tried commenting out the helmet middleware in my Koa server.js file and that did not help.
Note that if I open a new tab in the same browser session and go to localhost:8081/api/user/profile it works fine. It's only when calling this endpoint from Vue.js that it fails due to ctx.sesssion being null.
Any idea what resets the ctx.session to null?